BMP
文件结构
5 Y) A3 z/ M# {2 z- U
位图文件可看成由4个部分组成:位图文件头(bitmap-file header)、位图信息头( bitmap-information header )、彩色表(color table)和定义位图的字节阵列,它具有如下所示的形式。6 G0 K2 [/ `. N# `4 Q7 {, I B( H. J
位图文件的组成 | 结构名称 | 符号 | 位图文件头(bitmap-file header) | BITMAPFILEHEADER | bmfh | 位图信息头(bitmap-information header) | BITMAPINFOHEADER8 s( U6 F" ^( J# p | bmih | 彩色表(color table) | RGBQUAD" x& ^" R3 g0 [' c/ ]7 C* z | aColors[]* k, N! }, [8 p& G5 W$ K8 Z3 |/ s | 图象数据阵列字节 | BYTE | aBitmapBits[] |
* s; x2 k4 j% Z, t7 e
: p9 s7 @: i ^8 z5 Q" I- y
1.
位图文件头
6 }9 r/ p' s( B2 V6 T+ t
位图文件头包含有关于文件类型、文件大小、存放位置等信息,在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义:# [+ T' R3 w* z. ~4 f# V5 G
typedef struct tagBITMAPFILEHEADER { /* bmfh */ UINT bfType;! Z5 B: q: }) H$ p5 r DWORD bfSize; 5 z6 w) y. C+ i4 n) O7 O& B UINT bfReserved1; F) ~6 D, k$ G UINT bfReserved2; 9 }' U1 c3 |5 ^. `* A DWORD bfOffBits;6 F( ]# i6 \$ Q% \ } BITMAPFILEHEADER; ) P- Y. Q- e l8 c" A- J2 r, b* { |
. q- _3 T5 `4 J) F2 w2 p
$ C1 Q) D, v6 U) Q
bfType | 说明文件的类型.(该值必需是0x4D42,也就是字符'BM'。我们不需要判断OS/2的位图标识,这么做现在来看似乎已经没有什么意义了,而且如果要支持OS/2的位图,程序将变得很繁琐。所以,在此只建议你检察'BM'标识)4 F# G j3 D8 U: @2 }: w | bfSize | 说明文件的大小,用字节为单位 | bfReserved1 | 保留,必须设置为0 | bfReserved2 | 保留,必须设置为0 | bfOffBits | 说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。 |
0 Q7 }* }' l: g$ [, J& z
2.
位图信息头
位图信息用BITMAPINFO结构来定义,它由位图信息头(bitmap-information header)和彩色表(color table)组成,前者用BITMAPINFOHEADER结构定义,后者用RGBQUAD结构定义。BITMAPINFO结构具有如下形式:
typedef struct tagBITMAPINFO { /* bmi */ 8 f: y, j8 c9 w p1 D6 _/ W BITMAPINFOHEADER bmiHeader;. \: t; U. I5 T RGBQUAD bmiColors[1]; } BITMAPINFO; |
o- t" p8 s' _" L, ?
bmiHeader | 说明BITMAPINFOHEADER结构,其中包含了有关位图的尺寸及位格式等信息 | bmiColors | 说明彩色表RGBQUAD结构的阵列,其中包含索引图像的真实RGB值。 |
BITMAPINFOHEADER5 G0 W4 f* ?2 l) z4 {7 } 包含有位图文件的大小、压缩类型和颜色格式,其结构定义为:1 f# `2 t* w' h, e k# H- r5 v- v
7 C# }& G* v& s4 r* T/ E
typedef struct tagBITMAPINFOHEADER { /* bmih */ DWORD biSize; LONG biWidth; LONG biHeight; ( d- l1 u+ h) k! H WORD biPlanes; WORD biBitCount; DWORD biCompression; ! O |- b# u3 f; R# E0 q DWORD biSizeImage; ! p" C2 e _8 }9 g& d* Z% v LONG biXPelsPerMeter; # p& K: ^) z8 t4 f LONG biYPelsPerMeter; DWORD biClrUsed; : P, Y6 L6 s( I DWORD biClrImportant;5 G1 d8 B9 i# D4 x9 |2 t) o } BITMAPINFOHEADER; |
- Z" l- u* p6 x
biSize | 说明BITMAPINFOHEADER结构所需要的字数。注:这个值并不一定是BITMAPINFOHEADER结构的尺寸,它也可能是sizeof(BITMAPV4HEADER)的值,或是sizeof(BITMAPV5HEADER)的值。这要根据该位图文件的格式版本来决定,不过,就现在的情况来看,绝大多数的BMP图像都是BITMAPINFOHEADER结构的(可能是后两者太新的缘故吧:-)。 | biWidth | 说明图象的宽度,以象素为单位 | biHeight | 说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。(注:当高度值是一个负数时(正向图像),图像将不能被压缩(也就是说biCompression成员将不能是BI_RLE8或BI_RLE4)。 | biPlanes | 为目标设备说明位面数,其值将总是被设为1 | biBitCount | 说明比特数/象素,其值为1、4、8、16、24、或32 | biCompression | 说明图象数据压缩的类型。其值可以是下述值之一:
| BI_RGB:没有压缩; | 3 N, M* d) h4 Y: n | BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);( q" A4 S" Y8 i | r- G9 v* }6 C% B, E+ s | BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成# [& @! Y, p" a: o | / c2 y2 ^: a @ | BI_BITFIELDS:每个象素的比特由指定的掩码决定。 | | biSizeImage | 说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0 | biXPelsPerMeter | 说明水平分辨率,用象素/米表示 | biYPelsPerMeter | 说明垂直分辨率,用象素/米表示 | biClrUsed | 说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项) | biClrImportant | 说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
: y$ l% i+ s" }1 w9 M
: m" x! T" p* E$ I5 |
(1)
彩色表的定位
! @/ i! D: h# E+ G! a
应用程序可使用存储在biSize成员中的信息来查找在BITMAPINFO结构中的彩色表,如下所示:3 C) J! r' ]9 `) u' Z o% o5 P
pColor = ((LPSTR) pBitmapInfo + (WORD) (pBitmapInfo->bmiHeader.biSize))
7 a8 p+ A' x" h; C3 T5 O! L
(2) biBitCount
$ `/ `3 b- P4 p1 O
biBitCount=1 表示位图最多有两种颜色,缺省情况下是黑色和白色,你也可以自己定义这两种颜色。图像信息头装调色板中将有两个调色板项,称为索引0和索引1。图象数据阵列中的每一位表示一个象素。如果一个位是0,显示时就使用索引0的RGB值,如果位是1,则使用索引1的RGB值。$ ^7 b+ J& @2 C9 M: L$ j& |
9 V- k0 t, ^$ ~8 O) i
biBitCount=4 表示位图最多有16种颜色。每个象素用4位表示,并用这4位作为彩色表的表项来查找该象素的颜色。例如,如果位图中的第一个字节为0x1F,它表示有两个象素,第一象素的颜色就在彩色表的第2表项中查找,而第二个象素的颜色就在彩色表的第16表项中查找。此时,调色板中缺省情况下会有16个RGB项。对应于索引0到索引15。4 R9 `. I1 F5 p# r% a, ~
biBitCount=8 表示位图最多有256种颜色。每个象素用8位表示,并用这8位作为彩色表的表项来查找该象素的颜色。例如,如果位图中的第一个字节为0x1F,这个象素的颜色就在彩色表的第32表项中查找。此时,缺省情况下,调色板中会有256个RGB项,对应于索引0到索引255。6 u, H% O% ~1 t9 D8 R' x0 Q4 z
biBitCount=16 表示位图最多有216种颜色。每个色素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。这种格式也被称作555 16位位图。如果biCompression成员的值是BI_BITFIELDS,那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码“与”上像素值,从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。) K- X# S! |5 V' x: Q6 i
- m% ?7 @7 }& A4 D/ `" H
biBitCount=24 表示位图最多有224种颜色。这种位图没有调色板(bmiColors成员尺寸为0),在位数组中,每3个字节代表一个象素,分别对应于颜色R、G、B。6 D! z7 U A( Q; z# c, y
. V w- r; s7 w9 k4 n8 Q& ?, D
biBitCount=32 表示位图最多有232种颜色。这种位图的结构与16位位图结构非常类似,当biCompression成员的值是BI_RGB时,它也没有调色板,32位中有24位用于存放RGB值,顺序是:最高位—保留,红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果 biCompression成员的值是BI_BITFIELDS时,原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or 98)中,系统只接受888格式,也就是说三个掩码的值将只能是:0xFF0000、0xFF00、0xFF。而在NT系统中,你只要注意使掩码之间不产生重叠就行。(注:这种图像格式比较规整,因为它是DWORD对齐的,所以在内存中进行图像处理时可进行汇编级的代码优化(简单))。
6 w# O2 I8 t" Q ]
(3) ClrUsed
( ?4 C7 \: v5 M/ @* t
BITMAPINFOHEADER结构中的成员ClrUsed指定实际使用的颜色数目。如果ClrUsed设置成0,位图使用的颜色数目就等于biBitCount成员中的数目。请注意,如果ClrUsed的值不是可用颜色的最大值或不是0,则在编程时应该注意调色板尺寸的计算,比如在4位位图中,调色板的缺省尺寸应该是16*sizeof(RGBQUAD),但是,如果ClrUsed的值不是16或者不是0,那么调色板的尺寸就应该是ClrUsed*sizeof(RGBQUAD)。
; G% w; B% q9 ^5 \1 \
(4)
* L1 ]" u4 T& @9 c0 w) } 图象数据压缩
& S+ D3 b1 @8 g- S5 Z$ F: o
① BI_RLE8
8 y. q/ D' @2 ?3 j% s; n7 u
每个象素为8比特的RLE压缩编码,可使用编码方式和绝对方式中的任何一种进行压缩,这两种方式可在同一幅图中的任何地方使用。2 z/ E7 q& R' X" p0 y
4 E; W# s) s6 l
编码方式:由2个字节组成,第一个字节指定使用相同颜色的象素数目,第二个字节指定使用的颜色索引。此外,这个字节对中的第一个字节可设置为0,联合使用第二个字节的值表示:
) e! {7 q m# D* {, P- Z
4 g; N& K& }/ y! q, u | 第二个字节的值为0:行的结束。8 p) k h' D5 ?( ]9 O: k | / L l% T& t. G) x, ] | 第二个字节的值为1:图象结束。 8 U5 S' r' x; W0 y | 5 D) D* j# J- Q0 j6 j5 W; V | 第二个字节的值为2:其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。 ( Z" u- c5 R. |: s# O! z |
绝对方式:第一个字节设置为0,而第二个字节设置为0x03~0xFF之间的一个值。在这种方式中,第二个字节表示跟在这个字节后面的字节数,每个字节包含单个象素的颜色索引。压缩数据格式需要字边界(word boundary)对齐。下面的例子是用16进制表示的8-位压缩
# b1 {( X1 @/ C2 u
图象数据:
03 04 05 06 00 03 45 56 67 00 02 78 00 02 05 01 02 78 00 00 09 1E 00 01 这些压缩数据可解释为 :
压缩数据 | 扩展数据 | 03 04 U# f3 |, w7 D2 V | 04 04 04 | 05 06$ ?/ g: r6 m4 {2 a$ R | 06 06 06 06 06 1 l# \: {- M% q" E: ` | 00 03 45 56 67 00$ ?, w+ o, Q- @2 a | 45 56 67 ! L" c2 C% m' a | 02 78' c9 q9 S" {7 P; ]+ A% r$ ^ | 78 78 ( t& K+ K( D+ s/ B | 00 02 05 016 R* ~5 J5 @+ T- o- z0 } | 从当前位置右移5个位置后向下移一行 | 02 78 | 78 78 ( q/ @# X2 H" K- v3 }- x; q1 b | 00 00 | 行结束 | 09 1E | 1E 1E 1E 1E 1E 1E 1E 1E 1E , f) m) @' J; O8 u- |/ V | 00 01 | RLE编码图象结束! V9 z+ L0 w, w4 X& Q4 Y |
/ u% d- M N- S
② BI_RLE4
编码方式:由2个字节组成,第一个字节指定象素数目,第二个字节包含两种颜色索引,一个在高4位,另一个在低4位。第一个象素使用高4位的颜色索引,第二个使用低4位的颜色索引,第3个使用高4位的颜色索引,依此类推。
9 b( p0 B4 v7 p. t. m
绝对方式:这个字节对中的第一个字节设置为0,第二个字节包含有颜色索引数,其后续字节包含有颜色索引,颜色索引存放在该字节的高、低4位中,一个颜色索引对应一个象素。此外,BI_RLE4也同样联合使用第二个字节中的值表示:: c$ a9 s; o4 f5 \- L7 }4 }2 n7 U9 q
* J# D5 B2 S% F1 k/ i$ C
, T3 ]0 e; S' k" b2 m' i7 G6 `) X | 第二个字节的值为0:行的结束。5 P. h2 L. ^8 f. Q T5 K 5 x9 f( t T' o9 H% U | 2 h! r; \5 h. C, F( ? | 第二个字节的值为1:图象结束。 | % V. a/ H& f# o0 N | 第二个字节的值为2:其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。 / a6 I, Z9 y+ A" \3 q* X" c |
下面的例子是用16进制数表示的4-位压缩图象数据:4 O4 ]% Q2 U9 Y5 e2 e, M$ H! ^. a
03 04 05 06 00 06 45 56 67 00 04 78 00 02 05 01 04 78 00 00 09 1E 00 01
( |1 X* ~; Z/ E
这些压缩数据可解释为 :, c3 R8 Q* g* ]9 u" M
压缩数据 | 扩展数据 | 03 04. e3 e" q& Z' v* E8 C7 c) B | 0 4 0 | 05 062 z4 A6 X, F" H; \$ i; P5 u | 0 6 0 6 0 5 f" T' n. A9 y' L# C+ y, F | 00 06 45 56 67 00 | 4 5 5 6 6 7 M3 N* J f+ o( U! ` | 04 784 ~4 x8 a* V# v7 y- G! E | 7 8 7 8 | 00 02 05 01 | 从当前位置右移5个位置后向下移一行 | 04 785 b( q; f; [ ~+ s; Y | 7 8 7 8 | 00 003 v+ k1 g$ ^" ]9 T | 行结束9 L9 e1 h6 d* B" e | 09 1E | 1 E 1 E 1 E 1 E 1 3 U/ N* M+ Y9 X! q9 ~ | 00 01) V" l& e# Q3 Z' s8 ` | RLE图象结束 |
3.
彩色表
彩色表包含的元素与位图所具有的颜色数相同,象素的颜色用RGBQUAD结构来定义。对于24-位真彩色图象就不使用彩色表(同样也包括16位、和32位位图),因为位图中的RGB值就代表了每个象素的颜色。彩色表中的颜色按颜色的重要性排序,这可以辅助显示驱动程序为不能显示足够多颜色数的显示设备显示彩色图象。RGBQUAD结构描述由R、G、B相对强度组成的颜色,定义如下:) v, r- T! y+ l
' f( M. M# N i) b" F m: T* t) H
typedef struct tagRGBQUAD { /* rgbq */ BYTE rgbBlue; ; `' E! d. c: L( ] BYTE rgbGreen; BYTE rgbRed; # f% |8 k$ W! l z: S0 S5 t1 b$ a& { BYTE rgbReserved;' O5 f+ ?' f/ ` } RGBQUAD; |
rgbBlue( ^: B7 e4 V! z# i | 指定蓝色强度. h$ T5 k9 B2 N- L4 E( E | rgbGreen$ Y" r/ `% A" t9 k) |2 p" n | 指定绿色强度 | rgbRed | 指定红色强度 | rgbReserved | 保留,设置为08 |, \! R% D0 l, M+ L |
位图数据
紧跟在彩色表之后的是图象数据字节阵列。图象的每一扫描行由表示图象象素的连续的字节组成,每一行的字节数取决于图象的颜色数目和用象素表示的图象宽度。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素。(只针对与倒向DIB,如果是正向DIB,则扫描行是由顶向下存储的),倒向DIB的原点在图像的左下角,而正向DIB的原点在图像的左上角。同时,每一扫描行的字节数必需是4的整倍数,也就是DWORD对齐的。如果你想确保图像的扫描行DWORD对齐,可使用下面的代码:
(((width*biBitCount)+31)>>5)<<2
& ?. E0 |* \/ d; ^4 I
|