BMP是一种与硬件设备无关的图像文件格式,使用非常广。它采用位映射存储格式,除了图像深度可选以外,不采用其他任何压缩,因此,BMP文件所占用的空间很大。BMP文件的图像深度可选lbit、4bit、8bit及24bit。BMP文件存储数据时,图像的扫描方式是按从左到右、从下到上的顺序。 由于BMP文件格式是Windows环境中交换与图有关的数据的一种标准,因此在Windows环境中运行的图形图像软件都支持BMP图像格式。
组成
典型的BMP图像文件由四部分组成:
1:位图文件头数据结构,它包含BMP图像文件的类型、显示内容等信息;
2:位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
3:调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
4:位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
类型
位图一共有两种类型,即:设备相关位图(DDB)和设备无关位图(DIB)。DDB位图在早期的Windows系统(Windows3.0以前)中是很普遍的,事实上它也是唯一的。然而,随着显示器制造技术的进步,以及显示设备的多样化,DDB位图的一些固有的问题开始浮现出来了。比如,它不能够存储(或者说获取)创建这张图片的原始设备的分辨率,这样,应用程序就不能快速的判断客户机的显示设备是否适合显示这张图片。为了解决这一难题,微软创建了DIB位图格式。
设备无关位图(Device-IndependentBitmap)
DIB位图包含下列的颜色和尺寸信息:
*原始设备(即创建图片的设备)的颜色格式。
*原始设备的分辨率。
*原始设备的调色板
*一个位数组,由红、绿、蓝(RGB)三个值代表一个像素。
*一个数组压缩标志,用于表明数据的压缩方案(如果需要的话)。
以上这些信息保存在BITMAPINFO结构中,该结构由BITMAPINFOHEADER结构和两个或更多个RGBQUAD结构所组成。BITMAPINFOHEADER结构所包含的成员表明了图像的尺寸、原始设备的颜色格式、以及数据压缩方案等信息。RGBQUAD结构标识了像素所用到的颜色数据。
DIB位图也有两种形式,即:底到上型DIB(bottom-up),和顶到下型DIB(top-down)。底到上型DIB的原点(origin)在图像的左下角,而顶到下型DIB的原点在图像的左上角。如果DIB的高度值(由BITMAPINFOHEADER结构中的biHeight成员标识)是一个正值,那么就表明这个DIB是一个底到上型DIB,如果高度值是一个负值,那么它就是一个顶到下型DIB。注意:顶到下型的DIB位图是不能被压缩的。
位图的颜色格式是通过颜色面板值(planes)和颜色位值(bitcount)计算得来的,颜色面板值永远是1,而颜色位值则可以是1、4、8、16、24、32其中的一个。如果它是1,则表示位图是一张单色位图(译者注:通常是黑白位图,只有黑和白两种颜色,当然它也可以是任意两种指定的颜色),如果它是4,则表示这是一张VGA位图,如果它是8、16、24、或是32,则表示该位图是其他设备所产生的位图。如果应用程序想获取当前显示设备(或打印机)的颜色位值(或称位深度),可调用API函数GetDeviceCaps(),并将第二个参数设为BITSPIXEL即可。
显示设备的分辨率是以每米多少个像素来表明的,应用程序可以通过以下三个步骤来获取显示设备或打印机的水平分辨率:
1.调用GetDeviceCaps()函数,指定第二个参数为HORZRES。
2.再次调用GetDeviceCaps()函数,指定第二个参数为HORZSIZE。
3.用第一个返回值除以第二个返回值。即:GetDeviceCaps(hDC,HORZRES)/GetDeviceCaps(hDC,HORZSIZE);
应用程序也可以使用相同的三个步骤来获取设备的垂直分辨率,不同之处只是要将HORZRES替换为VERTRES,把HORZSIZE替换为VERTSIZE,即可。
调色板是被保存在一个RGBQUAD结构的数组中,该结构指出了每一种颜色的红、绿、蓝的分量值。位数组中的每一个索引都对应于一个调色板项(即一个RGBQUAD结构),应用程序将根据这种对应关系,将像素索引值转换为像素RGB值(真实的像素颜色)。应用程序也可以通过调用GetDeviceCaps()函数来获取当前显示设备的调色板尺寸(将该函数的第二个参数设为NUMCOLORS即可)。
Win32API支持位数据的压缩(只对8位和4位的底到上型DIB位图)。压缩方法是采用运行长度编码方案(RLE),RLE使用两个字节来描述一个句法,第一个字节表示重复像素的个数,第二个字节表示重复像素的索引值。有关压缩位图的详细信息请参见对BITMAPINFOHEADER结构的解释。
应用程序可以从一个DDB位图创建出一个DIB位图,步骤是,先初始化一些必要的结构,然后再调用GetDIBits()函数。不过,有些显示设备有可能不支持这个函数,你可以通过调用GetDeviceCaps()函数来确定一下(GetDeviceCaps()函数在调用时指定RC_DI_BITMAP作为RASTERCAPS的标志)。
应用程序可以用DIB去设置显示设备上的像素(译者注:也就是显示DIB),方法是调用SetDIBitsToDevice()函数或调用StretchDIBits()函数。同样,有些显示设备也有可能不支持以上这两个函数,这时你可以指定RC_DIBTODEV作为RASTERCAPS标志,然后调用GetDeviceCaps()函数来判断该设备是否支持SetDIBitsToDevice()函数。也可以指定RC_STRETCHDIB作为RASTERCAPS标志来调用GetDeviceCaps()函数,来判断该设备是否支持StretchDIBits()函数。
如果应用程序只是要简单的显示一个已经存在的DIB位图,那么它只要调用SetDIBitsToDevice()函数就可以。比如一个电子表格软件,它可以打开一个图表文件,在窗口中简单的调用SetDIBitsToDevice()函数,将图形显示在窗口中。但如果应用程序要重复的绘制位图的话,则应该使用BitBlt()函数,因为BitBlt()函数的执行速度要比SetDIBitsToDevice()函数快很多。
设备相关位图(Device-DependentBitmaps)
设备相关位图(DDB)之所以现在还被系统支持,只是为了兼容旧的Windows3.0软件,如果程序员现在要开发一个与位图有关的程序,则应该尽量使用或生成DIB格式的位图。
DDB位图是被一个单个结构BITMAP所描述,这个结构的成员标明了该位图的宽度、高度、设备的颜色格式等信息。
DDB位图也有两种类型,即:可废弃的(discardable)DDB和不可废弃的(nondiscardable)DDB。可废弃的DDB位图就是一种当系统内存缺乏,并且该位图也没有被选入设备描述表(DC)的时候,系统就会把该DDB位图从内存中清除(即废弃)。不可废弃的DDB则是无论系统内存多少都不会被系统清除的DDB。API函数CreateDiscardableBitmap()函数可用于创建可废弃位图。而函数CreateBitmap()、CreateCompatibleBitmap()、和CreateBitmapIndirect()可用于创建不可废弃的位图。
应用程序可以通过一个DIB位图而创建一个DDB位图,只要先初始化一些必要的结构,然后再调用CreateDIBitmap()函数就可以。如果在调用该函数时指定了CBM_INIT标志,那么这一次调用就等价于先调用CreateCompatibleBitmap()创建当前设备格式的DDB位图,然后又调用SetDIBits()函数转换DIB格式到DDB格式。(可能有些设备并不支持SetDIBits()函数,你可以指定RC_DI_BITMAP作为RASTERCAPS的标志,然后调用GetDeviceCaps()函数来判断一下)。
对应的数据结构
1:BMP文件组成
BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。
2:BMP文件头(14字节)
BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
其结构定义如下:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;//位图文件的类型,必须为BM(0-1字节)
DWORD bfSize;//位图文件的大小,以字节为单位(2-5字节)
WORD bfReserved1;//位图文件保留字,必须为0(6-7字节)
WORD bfReserved2;//位图文件保留字,必须为0(8-9字节)
DWORD bfOffBits;//位图数据的起始位置,以相对于位图(10-13字节)
//文件头的偏移量表示,以字节为单位
}BITMAPFILEHEADER;
3:位图信息头(40字节)
BMP位图信息头数据用于说明位图的尺寸等信息。
typedef struct tagBITMAPINFOHEADER{
DWORD biSize;//本结构所占用字节数(14-17字节)
LONG biWidth;//位图的宽度,以像素为单位(18-21字节)
LONG biHeight;//位图的高度,以像素为单位(22-25字节)
WORD biPlanes;//目标设备的级别,必须为1(26-27字节)
WORD biBitCount;//每个像素所需的位数,必须是1(双色),(28-29字节)
//4(16色),8(256色)或24(真彩色)之一
DWORD biCompression;//位图压缩类型,必须是0(不压缩),(30-33字节)
//1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
DWORD biSizeImage;//位图的大小,以字节为单位(34-37字节)
LONG biXPelsPerMeter;//位图水平分辨率,每米像素数(38-41字节)
LONG biYPelsPerMeter;//位图垂直分辨率,每米像素数(42-45字节)
DWORD biClrUsed;//位图实际使用的颜色表中的颜色数(46-49字节)
DWORD biClrImportant;//位图显示过程中重要的颜色数(50-53字节)
}BITMAPINFOHEADER;
4:颜色表
颜色表用于说明位图中的颜色,它有若干个表项,每一个表项是一个RGBQUAD类型的结构,定义一种颜色。RGBQUAD结构的定义如下:
typedef struct tagRGBQUAD{
BYTE rgbBlue;//蓝色的亮度(值范围为0-255)
BYTE rgbGreen;//绿色的亮度(值范围为0-255)
BYTE rgbRed;//红色的亮度(值范围为0-255)
BYTE rgbReserved;//保留,必须为0
}RGBQUAD;
颜色表中RGBQUAD结构数据的个数有biBitCount来确定:
当biBitCount=1,4,8时,分别有2,16,256个表项;
当biBitCount=24时,没有颜色表项。
位图信息头和颜色表组成位图信息,BITMAPINFO结构定义如下:
typedef struct tagBITMAPINFO{
BITMAPINFOHEADER bmiHeader;//位图信息头
RGBQUAD bmiColors[1];//颜色表
}BITMAPINFO;
5:位图数据
位图数据记录了位图的每一个像素值,记录顺序是在扫描行内是从左到右,扫描行之间是从下到上。位图的一个像素值所占的字节数:
当biBitCount=1时,8个像素占1个字节;
当biBitCount=4时,2个像素占1个字节;
当biBitCount=8时,1个像素占1个字节;
当biBitCount=24时,1个像素占3个字节;
Windows规定一个扫描行所占的字节数必须是
4的倍数(即以long为单位),不足的以0填充,
biSizeImage=((((bi.biWidth*bi.biBitCount)+31)&~31)/8)*bi.biHeight;
一个实际例子,选择一个24位深度的225×225位图,其文件大小为152,154字节,则
152,154=14+40+(225×3+1)×225
利用UltraEdit打开文件则看到
00000000h: 42 4D 5A 52 02 00 00 00 00 00 36 00 00 00 28 00 ;
00000010h: 00 00 E1 00 00 00 E1 00 00 00 01 00 18 00 00 00 ;
00000020h: 00 00 00 00 00 00 C4 0E 00 00 C4 0E 00 00 00 00 ;
00000030h: 00 00 00 00 00 00 2D 2D 2D 39 39 39 30 30 30 37 ;
00000040h: 37 37 2F 2F 2F 32 32 32 57 57 57 A8 A8 A8 C6 C6 ;
……
42 4D 为“BM”
5A 52 02 00 为0x0002525A即152,154字节
00 00 为保留
00 00 为保留
36 00 00 00 为0x00000036即54字节(14 + 40)
28 00 00 00 为0x00000028即40字节
E1 00 00 00 为0x000000E1即225宽度
E1 00 00 00 为0x000000E1即225高度
01 00 为目标设备的级别,必须为1
18 00 为24位深度图
00 00 00 00 为位图压缩类型
00 00 00 00 为位图的大小,此处不准
C4 0E 00 00 为3780位图水平分辨率
C4 0E 00 00 为3780位图垂直分辨率
00 00 00 00 为位图使用的实际颜色表中的颜色数,为0表示都使用
00 00 00 00 为位图显示过程中的重要颜色数,为0表示都重要
2D 2D 2D 位图RGB分量的开始
当利用绘图程序将文件保存为256色,即8位深度的位图后,文件大小变为52,378字节,即
52,378 = 14 + 50 + 256 × 4 + (225 + 3)× 225
00000000h: 42 4D 9A CC 00 00 00 00 00 00 36 04 00 00 28 00 ;
00000010h: 00 00 E1 00 00 00 E1 00 00 00 01 00 08 00 00 00 ;
00000020h: 00 00 64 C8 00 00 C4 0E 00 00 C4 0E 00 00 00 00 ;
00000030h: 00 00 00 00 00 00 00 00 00 00 00 00 80 00 00 80 ;
00000040h: 00 00 00 80 80 00 80 00 00 00 80 00 80 00 80 80 ;
9A CC 00 00 为52,378字节
36 04 00 00 为1078 = 14 + 40 + 256 × 4字节
08 00 为8位深度
64 C8 00 00 为51,300 = 228 × 225字节
00 00 00 00 起为颜色表项