bmp文件大体上分为四个部分:
bmp文件构成位图文件头BITMAPFILEHEADER位图信息头BITMAPINFOHEADER调色板Palette实际的位图数据ImageDate第一部分为位图文件头,位图文件头长度固定,为14个字节。
typdef struct { WORD bfType; //指定文件类型,必须是0x424d,即”BM” DWORD bfSize; //指定文件大小 WORD bfReserved1; //保留字 WORD bfReserved2; //保留字 DWORD bfOffBits; //为文件头到实际的位图数据的偏移字节数,即上图前三部分和 }bitmapFileHeader;第二部分为位图信息头,这个结构的长度也是固定的,为40个字节。
typedef struct { DWORD biSize; //指这个机构的长度,为40 LONG biWidth; //指定图像的宽度,单位是像素 LONG biHeight; //指定图像的高度,单位是像素 WORD biPlanes; //平面数,必须为1,不用考虑 WORD biBitCount; //指定表示颜色是要用到的位数,常用值为1(黑白二色图), //4(16色图),8(256色),24(真彩色图)(新的.bmp格式支持32位色) DWORD biCompression; //指定位图是否压缩,有效的值为BI_RGB,BI_RLE8,BI_RLE4,BI_BITFILELDS DWORD biSizeImage; //指定实际的位图数据占用的字节数,其实也可以从以下的公式计算出来: //biSizeImage=bitWidth’*biHeight,bitWidth‘为大于等于bitWidth的4的倍数 LONG biXPelsPerMeter; //指定设备的水平分辨率,单位是每米的图像个数 LONG biYPelsPerMeter; //同上,垂直分辨率 DWORD biClrUsed; //指定本图像实际用到的颜色数,如果该值为零,则用到的颜色数为2^biBitCount DWORD biClrImportant;// 指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的 }bitmapInfoHeader;第三部分为调色板(真彩图像不需要调色版,bitmapInfoHeader后直接是数据)调色板实际是一个数组,共有biClrUsed个元素(如果该值为0,则有2^biBitCount个元素),数组中每个元素的类型是一个RGBQUAD结构,占四个字节,其定义如下:
typedef struct { BYTE rgbBlue; //该颜色的蓝色分量 BYTE rgbGreen; //该颜色的绿色分量 BYTE rgbRed; //该颜色的红色分量 BYTE rgbReserved; //保留值 } RGBQUAD;第四部分为实际图像数据 对于用到调色版的位图,图像数据就是该像素颜在调色板中的索引值。对于真彩图,图像数据就是实际的R、G、B值。
需要注意两点: (1)每一行的字节数必须是4的整倍数,如果不是,则需要补齐。 (2)一般来说,.bmp文件的数据是从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个像素,然后是左边第二个像素……接下来是倒数第二行左边第一个像素,左边第二个像素……依次类推 ,最后得到的是最上面一行的最右一个像素。
以24位bmp图像为例
定义数据结构:
typedef struct { unsigned char bfType[2]; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; }bitmapFileHeader; typedef struct { unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPixPerMeter; long biYPixPerMeter; unsigned long biClrused; unsigned long biClrImportant; }bitmapInfoHeader;读入文件头:
//仅展示,不用于实验 int readFileHeader(FILE *fp,bitmapFileHeader*bfHeader){ fseek(fp,0,SEEK_SET); fread(bfHeader,sizeof(bitmapFileHeader),1,fp); /* int i; printf("读入文件头:"); char*p=(char*)bfHeader; for(i=0;i<sizeof(bitmapFileHeader);i++,p++){ printf("%02x ",*p); } printf("\n"); printf("文件头信息如下:\n"); printf("文件类型:%c%c\n",bfHeader->bfType[0],bfHeader->bfType[1]); printf("文件大小: %d 字节\n",bfHeader->bfSize); printf("位图数据偏移:%d\n",bfHeader->bfOffBits); */ return 0; }读信息头:
//仅展示,不用于实验 int readInfoHeader(FILE*fp,bitmapInfoHeader*biHeader){ fseek(fp,14,SEEK_SET); fread(biHeader,sizeof(bitmapInfoHeader),1,fp); /* int i; printf("读入位图信息头:\n"); char*p=(char*)biHeader; for(i=0;i<sizeof(bitmapInfoHeader);i++,p++){ printf("%02x ",*p); if(i%20==0&&i!=0) printf("\n"); } printf("\n"); printf("位图信息头信息如下:\n"); printf("bmp图像宽度:%d\n",biHeader->biWidth); printf("bmp图像高度:%d\n",biHeader->biHeight); printf("bmp图像颜色位数: %d\n",biHeader->biBitCount); printf("bmp图像实际数据占用字节:%d\n",biHeader->biSizeImage); */ return 0; }读入bmp图像数据,并展示:
#include<stdio.h> #include<stdlib.h> #include<malloc.h> #pragma pack(1) //这个选项挺重要,不加的话程序就会报错,原因参考 //https://blog.csdn.net/qq_44310495/article/details/109181857 typedef struct { unsigned char bfType[2]; unsigned long bfSize; unsigned short bfReserved1; unsigned short bfReserved2; unsigned long bfOffBits; }bitmapFileHeader; typedef struct { unsigned long biSize; long biWidth; long biHeight; unsigned short biPlanes; unsigned short biBitCount; unsigned long biCompression; unsigned long biSizeImage; long biXPixPerMeter; long biYPixPerMeter; unsigned long biClrused; unsigned long biClrImportant; }bitmapInfoHeader; int main(){ FILE *fp,*fp_txt; if((fp=fopen("d:\Temp\\skull.bmp","rb"))==NULL){ perror("can not open file!"); return -1; } bitmapFileHeader bfHeader; fread(&bfHeader,14,1,fp); bitmapInfoHeader biHeader; fread(&biHeader,40,1,fp); int imSize=biHeader.biSizeImage; int width=biHeader.biWidth; int height=biHeader.biHeight; int bitCount=biHeader.biBitCount; fseek(fp,bfHeader.bfOffBits,SEEK_SET); unsigned char*imageData=(unsigned char*)malloc(imSize*sizeof(unsigned char)); fread(imageData,imSize*sizeof(unsigned char),1,fp); //图像为24位图像 int lineBytes=(bitCount*width+31)/32*4;//得到图像数据的bitwidth' int i,j; int r,g,b; for(i=0;i<height;i++){ //对于每一行 for(j=0;j<width*3;j++){ //对于每一列 r=*(imageData+lineBytes*(height-1-i)+j); //从最后一行往上读 j++; g=*(imageData+lineBytes*(height-1-i)+j); j++; b=*(imageData+lineBytes*(height-1-i)+j); if(r==255&&g==255&&b==255) //模拟二值图像展示 printf(" "); else printf(".$"); } printf("\n"); } free(imageData); fclose(fp); getchar(); return 0; }代码效果展示: