BMP是英文Bitmap(位图)的简写,它是Windows操作系统中的标准图像文件格式,能够被多种Windows应用程序所支持。随着Windows操作系统的流行与丰富的Windows应用程序的开发,BMP位图格式理所当然地被广泛应用。这种格式的特点是包含的图像信息较丰富,几乎不进行压缩,但由此导致了它与生俱来的缺点–占用磁盘空间过大
bmp 格式图片的文件头长度绝大多数都是 54 字节,其中包括 14 字节的 位图文件头(head)以及 40 字节的 DIB (Device Independent Bitmap) 位图信息数据头(BItmap Information Header)。
地址(hex) | 字节长度(byte) | 描述 |
---|---|---|
00 | 2 | 固定头文件字段,内容为0x424D |
02 | 4 | bmp文件大小(little endian) |
06 | 2 | 预留字段 |
08 | 2 | 预留字段 |
0A | 4 | 图片信息的开始位置 |
地址(hex) | 字节长度(byte) | 描述 |
---|---|---|
0E | 4 | 位图信息数据头的大小 40bytes |
12 | 4 | 图像宽度(little endian) |
16 | 4 | 图像高度(little endian) |
1A | 2 | 色彩平面的数量,默认为1 |
1C | 2 | 每像素用多少bit表示 |
1E | 4 | 图片采用的压缩方式,通常不压缩即BL_RGB,对应值0 |
22 | 4 | 图片大小(原始位图数据大小)对于不压缩的图片,默认为0 |
26 | 4 | 横向分辨率(像素/米) |
2A | 4 | 纵向分辨率(像素/米) |
2E | 4 | 调试板中颜色数量,默认为0 |
32 | 4 | 重要颜色的数量,默认为0 |
拿最常见的 24BPP RGB (24 比特每像素,红绿蓝三通道) 位图来说,每种颜色需要 8 比特,或者说 1 字节,来存储。在二进制文件中,通常情况下,RGB 按照蓝、绿、红的顺序依次表示图片中的像素点,而 RGBA 则按照蓝、绿、红、透明的顺序(从左下开始,横向逐行向上扫描)。特殊时候,也会出现顺序与上述情况不同的特例,这时色彩顺序会写在 DIB Header 的 Bit Fields 中,以不同色彩通道的 Mask 的形式进行规定。由于 BI_BITFIELDS 也是一种压缩方式,而通常 BMP 不采用任何压缩方式,所以绝大多数时候,我们都是按照前面说的顺序进行排序。
地址(hex) | 字节长度(byte) | 描述(big endian) | |
---|---|---|---|
36 | 4 | 00 00 FF 00 | Red Channel bit mask |
3A | 4 | 00 FF 00 00 | Green Channel bit mask |
3E | 4 | FF 00 00 00 | Blue Channel bit mask |
42 | 4 | 00 00 00 FF | Alpha Channel bit mask |
主流的 CPU 每次从内存中读取并处理数据块(chunk),且通常为 32 比特(4 字节)。因此,为了提升读取效率,位图每行的数据(字节)都需要是 4 的倍数,字节对齐
Row_Size = (BitPerPixel * Image_Width + 31) / 32 * 4
每行的字节数等于:每像素比特数乘以图片宽度加 31 的和除以 32,并向下取整,最后乘以 4。
Pix_Array_Size = Row_Size * Image_Height
原始位图数据大小等于:每行的字节数乘以图像高度(也就是总行数)
文件大小 = 原始位图数据大小 + 文件头大小
#pragma pack(1)
typedef struct {
unsigned short bfType; // "BM"
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必须设置为0
unsigned short bfReserved2; // 保留,必须设置为0
unsigned int bfOffBits; // 从文件头到像素数据的偏移
} BitMapFileHeader;
typedef struct{
unsigned int biSize; // 此结构体的大小
int biWidth; // 图像的宽
int biHeight; // 图像的高
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1
unsigned short biBitCount; // 一像素所占的位数,一般为24
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits
int biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0
int biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。
unsigned int biClrImportant;// 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。
} BitMapInfoHeader;
// 24bit
typedef struct {
unsigned char rgblue;
unsigned char rgbgreen;
unsigned char rgbred;
unsigned char rgbreserved;
} PixelInfo;
#ifndef _BMP_H_
#define _BMP_H_
#pragma pack(1)
typedef struct {
unsigned short bfType; // "BM"
unsigned int bfSize; // 文件大小
unsigned short bfReserved1; // 保留,必须设置为0
unsigned short bfReserved2; // 保留,必须设置为0
unsigned int bfOffBits; // 从文件头到像素数据的偏移
} BitMapFileHeader;
typedef struct{
unsigned int biSize; // 此结构体的大小
int biWidth; // 图像的宽
int biHeight; // 图像的高
unsigned short biPlanes; // 表示bmp图片的平面属,显然显示器只有一个平面,所以恒等于1
unsigned short biBitCount; // 一像素所占的位数,一般为24
unsigned int biCompression; // 说明图象数据压缩的类型,0为不压缩。
unsigned int biSizeImage; // 像素数据所占大小, 这个值应该等于上面文件头结构中bfSize-bfOffBits
int biXPelsPerMeter; // 说明水平分辨率,用象素/米表示。一般为0
int biYPelsPerMeter; // 说明垂直分辨率,用象素/米表示。一般为0
unsigned int biClrUsed; // 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。
unsigned int biClrImportant;// 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。
} BitMapInfoHeader;
// 24bit
typedef struct {
unsigned char rgblue;
unsigned char rgbgreen;
unsigned char rgbred;
unsigned char rgbreserved;
} PixelInfo;
#endif
#include
#include "bmp.h"
void show_bmp_file_head(BitMapFileHeader *data)
{
printf("#########################################\n");
printf("bfType %x\n", data->bfType);
printf("bfSize %d\n", data->bfSize/1024);
printf("bfOffBits %d\n", data->bfOffBits);
}
void show_bmp_info_head(BitMapInfoHeader *data)
{
printf("#########################################\n");
printf("biSize %d\n", data->biSize);
printf("biWidth %d\n", data->biWidth);
printf("biHeight %d\n", data->biHeight);
printf("biPlanes %d\n", data->biPlanes);
printf("biBitCount %d\n", data->biBitCount);
printf("biCompression %d\n", data->biCompression);
printf("biXPelsPerMeter %d\n", data->biXPelsPerMeter);
printf("biYPelsPerMeter %d\n", data->biYPelsPerMeter);
printf("biClrUsed %d\n", data->biClrUsed);
printf("biClrImportant %d\n", data->biClrImportant);
}
void show_bmp_rgb(PixelInfo *data)
{
printf("#########################################\n");
printf("rgblue %2x\n", data->rgblue);
printf("rgbgreen %2x\n", data->rgbgreen);
printf("rgbred %2x\n", data->rgbred);
}
int main(int argc, char *argv[])
{
FILE *fp = NULL;
BitMapFileHeader FileHead;
BitMapInfoHeader InfoHead;
PixelInfo rgbinfo;
char *ptr = NULL;
int i, j;
fp = fopen(argv[1], "rb");
if(fp == NULL)
{
perror("open bmp file error");
return -1;
}
fread(&FileHead, sizeof(BitMapFileHeader), 1, fp);
fread(&InfoHead, sizeof(BitMapInfoHeader), 1, fp);
show_bmp_file_head(&FileHead);
show_bmp_info_head(&InfoHead);
show_bmp_rgb(&rgbinfo);
ptr = (unsigned char *)malloc(FileHead.bfSize);
if(ptr == NULL)
{
perror("malloc ptr fail\n");
return 0;
}
for(i = 0; i <= InfoHead.biHeight; i++)
{
for(j = 0; j <= InfoHead.biWidth; j++)
{
fread(&rgbinfo, sizeof(PixelInfo), 1, fp);
*ptr = 0xff << 24 | rgbinfo.rgbred << 16 | rgbinfo.rgbgreen << 8 | rgbinfo.rgblue;
memset(&rgbinfo, 0, sizeof(PixelInfo));
ptr++;
if((rgbinfo.rgbred == 255) && (rgbinfo.rgbgreen == 255) && (rgbinfo.rgblue == 255))
printf(" ");
else
printf("#");
}
printf("\n");
}
fclose(fp);
return 0;
}
lin@u-android:~/lin/workspace/demo_project/bmp$ gcc -o bmp_test show_bmp_head.c
lin@u-android:~/lin/workspace/demo_project/bmp$ ./bmp_test ./test.bmp
#########################################
bfType 4d42
bfSize 459
bfOffBits 54
#########################################
biSize 40
biWidth 507
biHeight 309
biPlanes 1
biBitCount 24
biCompression 0
biXPelsPerMeter 0
biYPelsPerMeter 0
biClrUsed 0
biClrImportant 0