首先了解一下BMP文件的格式,在网上也能找到很多的相关资料。
位图的格式可用下面的结构来表示:
a、BITMAPFILEHEADER bmfh;
b、BITMAPINFOHEADER bmih;
c、RGBQUAD aColors[];
d、BYTE aBitmapBits[];
A、第一部分为位 图文件头BITMAPFILEHEADER,是一个结构,其定义如下:
typedef struct tagBITMAPFILEHEADER{
unsigned short bfType; 2Bytes 指定文件类型,必须是42 4D,即字符串"BM"
unsigned int bfSize; 4Bytes 指定文件大小,包括这14 个字节
unsigned short bfReserved1; 2Bytes 为保留字,填充 00 00
unsigned short bfReserved2;2Bytes 为保留字,填充 00 00
unsigned int bfOffBits; 4Bytes 为从文件头到实际的位图数据的偏移字节数
} BITMAPFILEHEADER; //这个结构的长度为14 个字节 这一点很重要
之前可以用Windows自带的画图程序,画一个 600*400的BMP图像 test.bmp
在LINUX下用命令 od -x test.bmp >> test
vi test
可以看到头14 个字节为:
42 4D | 36 00 12 00 | 00 00 | 00 00 | 36 00 00 00
B、第二部分为位图信息头BITMAPINFOHEADER,也是一个结构,
其定义如下
typedef struct tagBITMAPINFOHEADER{
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;//这个结构的长度为40 个字节,
各个域的说明如下:
biSize 4Bytes 指定这个结构的长度,为0x28(40)个字节
biWidth 4Bytes 指定图象的宽度,单位是象素
biHeight 4Bytes 指定图象的高度,单位是象素
biplanes 2Bytes 必须是1
biBitCount 2Bytes 指定表示颜色时要用到的位数,常用的值为
1: 黑白二色图;
4: 16 色图;
8: 256 色图;
24: 24 位真彩色图(新的.bmp 格式支持32 位色)。
BiCompression 4Bytes 指定位图是否压缩,有效的值为
BI_RGB = 0,不压缩
BI_RLE8 = 1,RLE8 压缩
BI_RLE4 = 2,RLE4 压缩
BI_bitfields = 3
BiSizeImage 4Bytes 指定实际的位图数据占用的字节数,其实也可以从以下的公式
中计算出 来:biSizeImage=biWidth'*biHeight
(要注意的是:上述公式中的biWidth'必须是4 的整倍数(所以不是biWidth,而是biWidth',
表示大于或等于biWidth 的4 的整倍数。举个例子,如果biWidth=240,则biWidth'=240;
如果 biWidth=241,biWidth'=244)如果biCompression 为BI_RGB,则该项可能为零
biXPelsPerMeter 4Bytes 指定目标设备的水平分辨率,单位是每米的象素个数
biYPelsPerMeter 4Bytes 指定目标设备的垂直分辨率,单位是每米的象素个数
biClrUsed 4Bytes 指定本图象实际用到的颜色数,如果该值为零,则用到的颜色数为2 的biBitCount 次方
biClrImportant 4Bytes 指定本图象中重要的颜色数,如果该值为零,则认为所有的颜色都是重要的
vi test
可以看到这40 个字节的值。
C、第三部分为调色板(Palette),
当然,这里是对那些需要调色板的位图文件而言的。有些位图,如24真彩色图,前面已经讲过,是不需要调色板的,BITMAPINFOHEADER 后直接是位图数据。
调色板实际上是一个数组,共有biClrUsed 个元素(如果该值为零,则有2 的biBitCount次方个元素)。数组中每个元素的类型是一个RGBQUAD 结构,占4 个字节,其定义如下:
typedef struct tagRGBQUAD{
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
D、第四部分就是实际的图象数据了。对于用到调色板的位图,图象数 据就是该像素颜色在调色板中的索引值,对于真彩色图,图象数据就是实际的 R,G,B 值。下面就2 色,16 色,256 色位图和真彩色位图分别介绍。
对于2 色位图,用1 位就可以表示该像素的颜色(一般0 表示黑,1 表示白),所以一个字节可以表示8 个像素。
对于16 色位图,用4 位可以表示一个像素的颜色,所以一个字节可以表示2 个像素。
对于256 色位图,一个字节刚好可以表示1 个像素。
对于真彩色 图,三个字节才能表示1 个像素。
E、图象数据的注意事项
1、 图象数据的字节数必须是4 的整倍数,如果不是,则需要补齐。这在前面介绍biSizeImage 时已经提到了,使用索引色时位图中每行的数据也必须为4 个字节。2、BMP 文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个像素,然后是左边第二个像素⋯接下来是倒数第二行左边第一个 像素,左边第二个像素⋯依次类推,最后得到的是最上面一行的最右一个像素。
实现保存24 位真彩色位图 我的最终代码
#include "stdio.h"
#include "stdlib.h"
#define w 10
#define h 10
typedef long LONG;
typedef unsigned char BYTE;
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef struct {
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BMPFILEHEADER_T;
typedef struct{
DWORD biSize;
DWORD biWidth;
DWORD biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
DWORD biXPelsPerMeter;
DWORD biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BMPINFOHEADER_T;
void Snapshot( BYTE * pData, int width, int height, char * filename ,DWORD size)
{
// 位图第一部分,文件信息
BMPFILEHEADER_T bfh={0};
bfh.bfType = (WORD)0x4d42; //bm
bfh.bfSize = (DWORD)(size+54);
bfh.bfReserved1 = 0; // reserved
bfh.bfReserved2 = 0; // reserved
bfh.bfOffBits = 54;
// 位图第二部分,数据信息
BMPINFOHEADER_T bih={0};
bih.biSize = 40;
bih.biWidth = width;
bih.biHeight = height;
bih.biPlanes = 1;
bih.biBitCount = 24; //24真彩色位图
bih.biCompression = 0;
bih.biSizeImage = 0;
bih.biXPelsPerMeter = 0;
bih.biYPelsPerMeter = 0;
bih.biClrUsed = 0;
bih.biClrImportant = 0;
FILE * fp = fopen(filename,"wb");
if( !fp ) return;
fwrite( &bfh.bfType,1,2,fp );
fwrite( &bfh.bfSize,1,4,fp );
fwrite( &bfh.bfReserved1,1,2,fp );
fwrite( &bfh.bfReserved2,1,2,fp );
fwrite( &bfh.bfOffBits,1,4,fp );
//这里一定要特别注意,如果使用 fwrite( &bih,1,sizeof(BMPFILEHEADER_T),fp );因为头信息是14位,使用这个语句填充的是16位,图像显示的是黑色的。所以要按照位数填充,以免自动补齐,这个问题也是我通过对比正确的BMP文件 和我生成的bmp文件的二进制格式位数,发现前面几位就是错位的,网上很多资料写的都实现不了,不知道是什么问题。
fwrite( &bih,1,sizeof(BMPINFOHEADER_T),fp );
fwrite(pData,1,size,fp);
fclose( fp );
}
// 位图第二部分,数据信息
void SaveFile()
{
unsigned char *bits;//定义指向位图数据的指针
DWORD size=4*((w*24+31)/32) * h; //也是为了保证是四的倍数
bits = (unsigned char *)malloc(size);//为位图分配内存空间并赋值给bits
glReadPixels(0,0,w,h,GL_BGR_EXT,GL_UNSIGNED_BYTE,bits);//从帧缓存中读取位图数据
Snapshot(( BYTE*)bits,w,h,"test.bmp",size);// 生成24位BMP图片
}