BMP取自位图BitMaP的缩写,也称为DIB(与设备无关的位图),是微软视窗图形子系统(Graphics Device Interface)内部使用的一种位图图形格式,它是微软视窗平台上的一个简单的图形文件格式。
详细介绍可以参考维基百科:http://zh.wikipedia.org/wiki/BMP
学习的目的是解析一个为图文件并显示位图。在位图图像数据之前,有文件头和信息头,Windows环境下wingdi.h中提供了相应的数据结构。
文件头结构体:
typedef struct tagBITMAPFILEHEADER { WORD bfType;//位图文件的类型,必须为BM(1-2字节) DWORD bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前) WORD bfReserved1;//位图文件保留字,必须为0(7-8字节) WORD bfReserved2;//位图文件保留字,必须为0(9-10字节) DWORD bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前) //文件头的偏移量表示,以字节为单位 }BITMAPFILEHEADER;
信息头结构体:
typedef struct tagBITMAPINFOHEADER{ DWORD biSize;//本结构所占用字节数(15-18字节) LONG biWidth;//位图的宽度,以像素为单位(19-22字节) LONG biHeight;//位图的高度,以像素为单位(23-26字节) WORD biPlanes;//目标设备的级别,必须为1(27-28字节) WORD biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节) //4(16色),8(256色)16(高彩色)或24(真彩色)之一 DWORD biCompression;//位图压缩类型,必须是0(不压缩),(31-34字节) //1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一 DWORD biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节) LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(39-42字节) LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(43-46字节) DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(47-50字节) DWORD biClrImportant;//位图显示过程中重要的颜色数(51-54字节) }BITMAPINFOHEADER;
全部实例代码:
/* * bmptest.c * * Created on: 2014-10-27 * Author: HZY */ #include <stdio.h> #include "GL/freeglut.h" static BYTE* buffer = NULL; static BITMAPINFOHEADER infoHeader; //BGR转RGB:对于每个点,第一字节和第三字节交换 void bgrToRgb(BYTE* buffer, int size) { int i = 0; int j = 0; BYTE temp = 0; int blueIdx = 0; int rowBytes = size / infoHeader.biHeight; for (i = 0; i < infoHeader.biHeight; i++) { for (j = 0; j < infoHeader.biWidth; j++) { blueIdx = i * rowBytes + j * 3; temp = buffer[blueIdx]; buffer[blueIdx] = buffer[blueIdx + 2]; buffer[blueIdx + 2] = temp; } } } //获取每一行所用的字节数,需要凑足4的倍数 int getRowBytes(int width){ //刚好是4的倍数 if((width * 3) % 4 == 0){ return width * 3; }else{ return ((width * 3) / 4 + 1) * 4; } } void printFileHeader(BITMAPFILEHEADER* h) { printf("$fileHeader: offbits[%ld], size[%ld], type[%d]\n", h->bfOffBits, h->bfSize, h->bfType); } void printInfoHeader(BITMAPINFOHEADER* h) { printf("$infoHeader: size[%ld], width[%ld], height[%ld], planes[%d]\n\t" "bitcount[%d], compression[%ld], sizeImage[%ld]\n\t" "XpixPerM[%ld], YpixPerM[%ld], colors[%ld], impColors[%ld]\n", h->biSize, h->biWidth, h->biHeight, h->biPlanes, h->biBitCount, h->biCompression, h->biSizeImage, h->biXPelsPerMeter, h->biYPelsPerMeter, h->biClrUsed, h->biClrImportant); } void parseBmp() { FILE* fp = NULL; BITMAPFILEHEADER fileHeader; int realBytes = 0; //打开文件 fp = fopen("test.bmp", "rb"); if (fp == NULL) { printf("file open error!\n"); exit(EXIT_FAILURE); } //读取文件头 fread(&fileHeader, sizeof(BITMAPFILEHEADER), 1, fp); printFileHeader(&fileHeader); //读取信息头 fread(&infoHeader, sizeof(BITMAPINFOHEADER), 1, fp); printInfoHeader(&infoHeader); //只处理24位非压缩位图 switch (infoHeader.biBitCount) { case 24: realBytes = infoHeader.biWidth * infoHeader.biHeight * 3; printf("image realBytes[%d]\n", realBytes); //一些图片编辑软件得到的biSizeImage为0, //这样的图片手动计算biSizeImage if (infoHeader.biSizeImage < realBytes) { infoHeader.biSizeImage = getRowBytes(infoHeader.biWidth) * infoHeader.biHeight; printf("Calculated biSizeImage[%ld]\n", infoHeader.biSizeImage); } //获取堆空间 buffer = malloc(infoHeader.biSizeImage); //读数据到内存 fread(buffer, infoHeader.biSizeImage, 1, fp); //bmp中保存的是BGR,转成RGB,方便显示 bgrToRgb(buffer, infoHeader.biSizeImage); break; default: printf("not 24 bit bitmap!\n"); fclose(fp); exit(EXIT_FAILURE); break; } fclose(fp); } //opengl显示图片 void myDisplay(){ glClear(GL_COLOR_BUFFER_BIT); glDrawPixels(infoHeader.biWidth, infoHeader.biHeight, GL_RGB, GL_UNSIGNED_BYTE, buffer); glFlush(); } int main(int c, char** v){ parseBmp(); glutInit(&c, v) ; glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(infoHeader.biWidth, infoHeader.biHeight); glutCreateWindow("hello bmp"); glutDisplayFunc(&myDisplay); glutMainLoop(); free(buffer); return 0; }
效果如图:
显示图片采用opengl来渲染,使用了freeglut:
工程是用eclipse中cdt使用mingw编译:
全部代码在附件中。