BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的开发,BMP位图格式理所当然地被广泛应用。这种格式的特点是包含的图像信息较丰富,几乎不进行压缩,但由此导致了它与生俱生来的缺点–占用磁盘空间过大。所以,目前BMP在单机上比较流行。
BMP文件由4部分组成:
1.位图文件头(bitmap-file header)(大小为14Byte)
2.位图信息头(bitmap-informationheader)(大小为40Byte)
3.颜色表(color table)
4.颜色点阵数据(bits data)
(24位真彩色位图没有颜色表,所以只有1、2、4这三部分。)
色彩深度又叫色彩位数,即位图中要用多少个二进制位来表示每个点的颜色,是分辨率的一个重要指标。常用有1位(单色),2位(4色,CGA),4位(16色,VGA),8位(256色),16位(增强色),24位和32位(真彩色)等。
BMP文件记录一行图像是以字节为单位的。因此,就不存在一个字节中的数据位信息表示的点在不同的两行中。也就是说,设显示模式位16色,在每个字节分配两个点信息时,如果图像的宽度位奇数,那么最后一个像素点的信息将独占一个字节,这个字节的后4位将没有意义。接下来的一个字节将开始记录下一行的信息。
为了显示的方便,除了真彩色外,其他的每种颜色模式的行字节数要用数据“00”补齐为4的整数倍。如果显示模式为16色(2个字节),当图像宽为19时,存储时每行则要补充4-(19/2+1)%4=2个字节。如果显示模式为256色,当图像宽为19时,每行也要补充4-19%4=1个字节。
位图行四字节对齐算法:
第一种表示方法:
LineByte = [(biWidth * biBitCount + 7) / 8 +3 ])/ 4 * 4
第二种表示方法:
LineByte = (biWidth * biBitCount + 31) /32 *4
LineByte :每行所占的字节
biWidth : 图片像素宽度
biBitCount :像素深度,1bit,4bit,8bit…
biWidth * biBitCount :未填充时一行数据所占的位数
(biWidth * biBitCount+7) / 8 :未对齐时一行数据所占的字节数
[(biWidth * biBitCount + 7) / 8 +3 ])/ 4 :对齐时一行数据所占的字节单元(4Byte)数
(biWidth * biBitCount + 31) /32 :对齐时一行数据所占的字节单元(4Byte)数
1色黑白:
文件头(14字节) + 信息头(40字节) + 2个调色板(共8字节) + Height(图像高度) * (Width + 8 - Width % 8) / 8
16色:
文件头(14字节) + 信息头(40字节) + 16个调色板(共64字节) + Height(图像高度) * (Width + 4 - Width % 4) / 2
256色:
文件头(14字节) + 信息头(40字节) + 256个调色板(共1024字节) + Height(图像高度) * (Width + 4 - Width % 4)
16位色:
文件头(14字节) + 信息头(40字节) + Height(图像高度) * (Width + 4 - Width % 4) * 2 (由于每个像素由两个字节表示)
24位色:
文件头(14字节) + 信息头(40字节) + Height(图像高度) * (Width + 4 - Width % 4) * 3 (由于每个像素由三个字节表示)
1、RGB数据也是倒着念的,原始数据是按B、G、R的顺序排列的。
2、位图全部的像素,是按照自下向上,自左向右的顺序排列的。
即:BMP内存第0行,是真实图像下面的最后一行。
举例,假如图像为2*2大小,像素三颜色按照RGB的顺序, 我们看到的图像为:
1 2 3, 11 22 33
4 5 6, 44 55 66
内存表示如下:
6 5 4, 66 55 44 (0 0) – 第0行
3 2 1, 33 22 11 (0 0) – 第1行
注意,通常内存是需要内存对齐的,所以每行后面可能会有对齐所产生的0.
#include
#include
typedef struct tagBITMAPFILEHEADER
{
unsigned short int bfType; //位图文件的类型,必须为BM
unsigned long bfSize; //文件大小,以字节为单位
unsigned short int bfReserverd1; //位图文件保留字,必须为0
unsigned short int bfReserverd2; //位图文件保留字,必须为0
unsigned long bfbfOffBits; //位图文件头到数据的偏移量,以字节为单位
}BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER
{
long biSize; //该结构大小,字节为单位
long biWidth; //图形宽度以象素为单位
long biHeight; //图形高度以象素为单位
short int biPlanes; //目标设备的级别,必须为1
short int biBitcount; //颜色深度,每个象素所需要的位数
short int biCompression; //位图的压缩类型
long biSizeImage; //位图的大小,以字节为单位
long biXPelsPermeter; //位图水平分辨率,每米像素数
long biYPelsPermeter; //位图垂直分辨率,每米像素数
long biClrUsed; //位图实际使用的颜色表中的颜色数
long biClrImportant; //位图显示过程中重要的颜色数
}BITMAPINFOHEADER;
typedef struct
{
BITMAPFILEHEADER file; //文件信息区
BITMAPINFOHEADER info; //图象信息区
}bmp;
bmp readbmpfile(void); //函数声明
int main(void){
bmp m; //定义一个结构变量
m=readbmpfile(); //读取一个位图
getchar();
return 0;
}
bmp readbmpfile(void) {
bmp m; //定义一个位图结构
FILE *fp;
if((fp=fopen( "d:\\1.bmp", "r"))==NULL)
{ printf( "can't open the bmp imgae.\n ");
exit(0);
}
else
{
fread(&m.file.bfType,sizeof(char),1,fp);
printf("类型为%c",m.file.bfType);
fread(&m.file.bfType,sizeof(char),1,fp);
printf("%c\n",m.file.bfType);
fread(&m.file.bfSize,sizeof(long),1,fp);
printf("文件长度为%d\n",m.file.bfSize);
fread(&m.file.bfReserverd1,sizeof(short int),1,fp);
printf("保留字1为%d\n",m.file.bfReserverd1);
fread(&m.file.bfReserverd2,sizeof(short int),1,fp);
printf("保留字2为%d\n",m.file.bfReserverd2);
fread(&m.file.bfbfOffBits,sizeof(long),1,fp);
printf("偏移量为%d\n",m.file.bfbfOffBits);
fread(&m.info.biSize,sizeof(long),1,fp);
printf("此结构大小为%d\n",m.info.biSize);
fread(&m.info.biWidth,sizeof(long),1,fp);
printf("位图的宽度为%d\n",m.info.biWidth);
fread(&m.info.biHeight,sizeof(long),1,fp);
printf("位图的高度为%d\n",m.info.biHeight);
fread(&m.info.biPlanes,sizeof(short),1,fp);
printf("目标设备位图数%d\n",m.info.biPlanes);
fread(&m.info.biBitcount,sizeof(short),1,fp);
printf("颜色深度为%d\n",m.info.biBitcount);
fread(&m.info.biCompression,sizeof(long),1,fp);
printf("位图压缩类型%d\n",m.info.biCompression);
fread(&m.info.biSizeImage,sizeof(long),1,fp);
printf("位图大小%d\n",m.info.biSizeImage);
fread(&m.info.biXPelsPermeter,sizeof(long),1,fp);
printf("位图水平分辨率为%d\n",m.info.biXPelsPermeter);
fread(&m.info.biYPelsPermeter,sizeof(long),1,fp);
printf("位图垂直分辨率为%d\n",m.info.biYPelsPermeter);
fread(&m.info.biClrUsed,sizeof(long),1,fp);
printf("位图实际使用颜色数%d\n",m.info.biClrUsed);
fread(&m.info.biClrImportant,sizeof(long),1,fp);
printf("位图显示中比较重要颜色数%d\n",m.info.biClrImportant);
}
return m;
}
// transform 32-bit bitmap format to 24-bit bitmap format
void Bmp32ToBmp24(char Filename[])
{
char Filename2[] = "output.bmp";
//注意:如果没有LR_CREATEDIBSECTION,位图颜色将被映射到屏幕DC颜色
//也就是说,如果屏幕是16位颜色,则所有的图像都将映射到16位颜色
HBITMAP hbmp32 = (HBITMAP) LoadImage(NULL, Filename,
IMAGE_BITMAP, 0, 0,
LR_LOADFROMFILE |
LR_CREATEDIBSECTION);
BITMAP bmp;//获取位图信息
GetObject(hbmp32, sizeof(BITMAP), &bmp);
printf("Image Bit Depth : %dnWidth : %d , Height : %d n",
bmp.bmBitsPixel, bmp.bmWidth, bmp.bmHeight);//显示位图颜色模式和图像宽高
//计算24位图像每行的字节数
int BytesPerLine = 3 * bmp.bmWidth;
while(BytesPerLine % 4 != 0)
BytesPerLine ++;
BITMAPINFOHEADER bih = {0};//位图信息头
bih.biBitCount = 24;//每个像素字节大小
bih.biCompression = BI_RGB;
bih.biHeight = bmp.bmHeight;//高度
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
bih.biSizeImage = BytesPerLine * bmp.bmHeight;//图像数据大小
bih.biWidth = bmp.bmWidth;//宽度
BITMAPFILEHEADER bfh = {0};//位图文件头
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);//到位图数据的偏移量
bfh.bfSize = bfh.bfOffBits + bih.biSizeImage;//文件总的大小
bfh.bfType = (WORD)0x4d42;
FILE *fp = fopen(Filename2, "w+b");
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);//写入位图文件头
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);//写入位图信息头
byte * p = new byte[bih.biSizeImage];
//获取当前32位图像数据
GetDIBits(GetDC(NULL), hbmp32, 0, bmp.bmHeight, p, (LPBITMAPINFO)&bih, DIB_RGB_COLORS);
//只取rgb值,存入文件
byte b = 0;//用于填充
for(int i = 0 ; i < bmp.bmWidth * bmp.bmHeight ; i ++)
{
//32位位图图像的格式为:Blue, Green, Red, Alpha
fwrite(&(p[i * 3]), 1, 3, fp);
if(i % bmp.bmWidth == bmp.bmWidth - 1)//填充字节
{
for(int k = 0 ; k < (BytesPerLine - bmp.bmWidth * 3) ; k ++)
fwrite(&b, sizeof(byte), 1, fp);
}
}
delete [] p;
fclose(fp);
DeleteObject(hbmp32);
}