BMP位图图像格式简介

BMP位图图像格式简介

1. 文件结构

 

位图文件可看成由4个部分组成:位图文件头(bitmap-fileheader)、位图信息头(bitmap-informationheader)、彩色表(colortable)和定义位图的字节阵列,它具有如下所示的形式。

 

位图文件的组成

结构名称

符号

位图文件头(bitmap-file header)

BITMAPFILEHEADER

bmfh

位图信息头(bitmap-information header)

BITMAPINFOHEADER

bmih

彩色表(color table)

RGBQUAD

aColors[]

图象数据阵列字节

BYTE

aBitmapBits[]

 

位图文件结构可综合在表1中。

 

 

偏移量

域的名称

大小

内容

图像文件头

0000h

文件标识

2 byte

两字节的内容用来识别位图的类型:

‘BM’ : Windows 3.1x, 95, NT, …

‘BA’ :OS/2 Bitmap Array

‘CI’ :OS/2 Color Icon

‘CP’ :OS/2 Color Pointer

‘IC’ : OS/2 Icon

‘PT’ :OS/2 Pointer

注:因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行

0002h

File Size

1 dword

用字节表示的整个文件的大小

0006h

Reserved

1 dword

保留,必须设置为0

000Ah

Bitmap Data Offset

1 dword

从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量

图像信息头

000Eh

Bitmap Header Size

1 dword

位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:

28h - Windows 3.1x, 95, NT, …

0Ch - OS/2 1.x

F0h - OS/2 2.x

注:在Windows95、98、2000等操作系统中,位图信息头的长度并不一定是28h,因为微软已经制定出了新的BMP文件格式,其中的信息头结构变化比较大,长度加长。所以最好不要直接使用常数28h,而是应该从具体的文件中读取这个值。这样才能确保程序的兼容性。

0012h

Width

1 dword

位图的宽度,以象素为单位

0016h

Height

1 dword

位图的高度,以象素为单位

001Ah

Planes

1 word

位图的位面数(注:该值将总是1)

001Ch

Bits Per Pixel

1 word

每个象素的位数。

1 - 单色位图(实际上可有两种颜色,缺省情况下是黑色和白色。你可以自己定义这两种颜色)

4 - 16 色位图

8 - 256 色位图

16 - 16bit 高彩色位图

24 - 24bit 真彩色位图

32 - 32bit 增强型真彩色位图

001Eh

Compression

1 dword

压缩说明:

0 - 不压缩 (使用BI_RGB表示)

1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)

2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)

3 - Bitfields-位域存放方式(用BI_BITFIELDS表示)

0022h

Bitmap Data Size

1 dword

用字节数表示的位图数据的大小。该数必须是4的倍数

0026h

HResolution

1 dword

用象素/米表示的水平分辨率

002Ah

VResolution

1 dword

用象素/米表示的垂直分辨率

002Eh

Colors

1 dword

位图使用的颜色数。如8-比特/象素表示为100h或者 256

0032h

Important

1 dword

指定重要的颜色数。当该域的值等于颜色数时(或者等于0时),表示所有颜色都一样重要

调色板数据

根据BMP版本的不同而不同

Palette

N * 4 byte

调色板规范。对于调色板中的每个表项,这4个字节用下述方法来描述RGB的值:

  1字节用于蓝色分量

1字节用于绿色分量

1字节用于红色分量

1字节用于填充符(设置为0)

图象数据

根据BMP版本及调色板尺寸的不同而不同

Bitmap Data

xxx bytes

该域的大小取决于压缩方法及图像的尺寸和图像的位深度,它包含所有的位图数据字节,这些数据可能是彩色调色板的索引号,也可能是实际的RGB值,这将根据图像信息头中的位深度值来决定。

 

2 四个部分在位图图像数据中的相应位置,(位置偏移均以位图数据开始处为基准)

  起始位置偏移 <= 各部分数据具体存放位置 < 结束位置偏移

   第一部分,图像头:

起始位置偏移 0,

长度:0x0EH (2byte + 3 * dword = 14)

结束位置偏移:起始位置偏移 + 长度

第二部分,图像信息头:

起始位置偏移:上一部分结束位置偏移

长度:从 0x0EH 处读取到的 dword 的数据值

结束位置偏移:起始位置偏移 + 长度

 

       第三部分,调色板:

起始位置偏移:上一部分结束位置偏移

长度:从 0x0AH 处读取到的 dword 的数据值- 起始位置偏移

结束位置偏移:起始位置偏移 + 长度

 

       第四部分,位图数据:

              起始位置偏移:上一部分结束位置偏移

              长度:从 0x22H 处读取到的 dword 的数据值

              结束位置偏移:文件结束

 

3 单色位图图像数据的表示方法

   在单色位图图像中,只有两种颜色,黑色或白色,每一个像素只需要一个比特就能够完成表示,为了清楚比特0或1具体表示哪一种颜色,可以通过查询调色板。

   在单色位图图像中,调色板只包含两种颜色,每一种颜色用R G B 0 四个字节表示 (在实际的字节流中,顺序是 B G R 0)

   所以,位图图像数据中的0 代表调色板中 第一种颜色的颜色值, 1 代表调色板中 第二种颜色的颜色值。

 

4 C/C++中数据类型的长度

       byte: 1个字节, 8位(比特)

       word: 2个字节,由 unsigned short定义

       dword:4 个字节,由 unsigned long定义

 

5 根据前面的位图文件结构表,可以通过自定义数据结构 struct的方式来读取 相应的数据。

 

6 位图数据的存储方式:(自下而上,从左到右)

       扫描行是由底向上存储的,这就是说,位图数据的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素。

 

一行单色位图数据的存储格式规定

       每一扫描行的字节数必需是4的整倍数,当不够4的整数倍时,需要加0补齐

 

以 720 × 450 的单色位图图像为例

   水平扫描行的长度为720,则需要720比特来表示一个扫描行,即需要 720/8=90字节来表示,但是 90不是 4 的整数倍,因此需要用0补齐,直至为4的整数倍,即需要额外的2个填充字节。

   最终,长度为720的水平扫描行使用了 92 个字节来表示。

 

仅考虑分辨率为 256×192  128×96 两种模式

BMP文件分析(一)---单色BMP文件 收藏

最近需要用到BMP文件信息,参考网上的一些资料,把自己理解的东西整理一下。呵呵

 

以下是以单色bmp为例子分析,其中bmp中的头部8个pixel与尾部8个pixel画上了黑点

 

42 4D 3E 04 00 00 00 00  00 00 3E 00 00 00 28 00

00 00 80 00 00 00 40 00  00 00 01 00 01 00 00 00

00 00 00 04 00 00 C4 0E  00 00 C4 0E 00 00 00 00

00 00 00 00 00 00 00 00  00 00 FF FF FF 00 FF FF

FF FF FF FF FF FF FF FF  FF FF FF FF FF 00 FF FF  尾部

           ~~

.......................  .......................

.......................  .......................

FF FF FF FF FF FF FF FF  FF FF FF FF FF FF 00 FF  头部

              ~~

FF FF FF FF FF FF FF FF  FF FF FF FF FF FF

 

“~~”为头尾的8个pixel为黑点,头尾顺序反转,同一行也顺序反转

由于128÷8 = 16,所以每行就用16位来控制

 

------------------

------------------

42 4D  2 bytes 文件类型BM

3E 04 00 00   1 dword 文件大小1086

00 00 00 00   1 dword 保留,必须设置为0(include reserved1+reserved2)

3E 00 00 00   1 dword 从文件开始到位图数据开始之间的数据(bitmap data)之间的偏移量 3E(H) = 62(D)

---------------

28 00 00 00   1 dword 位图信息头(Bitmap Info Header)的长度,用来描述位图的颜色、压缩方法等。下面的长度表示:28h - Windows 3.1x, 95, NT, …

80 00 00 00   1 dword 位图的宽度,以象素为单位bmp的长128

40 00 00 00   1 dword 位图的高度,以象素为单位bmp的宽64

01 00   1 word 位图的位面数(注:该值将总是1Pages =1

01 00          1 word 每个象素的位数,1 - 单色位图Colors=2(BLACK & WHITE)

00 00 00 00   1 dword 压缩说明,0 - 不压缩 (使用BI_RGB表示)

00 04 00 00   1 dword 用字节数表示的位图数据的大小。该数必须是4的倍数 1024 =128*64/8 (长×宽÷每位表示8个pixel)

C4 0E 00 00   1 dword 用象素/米表示的水平分辨率,水平3780

C4 0E 00 00   1 dword 用象素/米表示的垂直分辨率,垂直3780

00 00 00 00      1 dword 位图使用的颜色数

00 00 00 00     1 dword 指定重要的颜色数

---------------

00 00 00 00   N * 4 byte 调色板规范。对于调色板中的每个表项,这4个字节用下述方法来描述RGB的值,根据BMP版本的不同而不同

FF FF FF 00     这里的N=2

 

从这里开始为bmp数据,可以根据偏移量得到3E(H) = 62(D)

 

-----------------------

-----------------------

数据段取值规律

每8个pixel由1个byte来控制从FF--00

FF  7F   3F   1F 0F  07  03 01  00

255 127  63  31 15  7  3 1  0

11111111 01111111 00111111 00011111 0000111100000111 00000011 00000001 00000000

由上述规律可以得到

当取出一个byte的数据要进行以下转换才知道那个pixel被画黑

1、从16进制到10进制转换

2、从10进制到2进制转换

将得到的2进制数中为0的pixel画黑

 

算法实现

1、以二进制文件打开文件

2、按照BMP文件信息和BMP图片信息开始读取,这里的长度是固定的

3、根据BMP文件信息中的数据偏移量得到,数据段的开始位置

4、读取数据信息根据每行用16位来描述以及数据反转的原因,故每次读取16个byte来进行每行的数据处理(可以使用递归)

5、用一个数组来存储bmp数据信息BMPINFO[128][64]

6、每byte都为8个pixel的信息

 

--------------------------------------------

--------------------------------------------

通用数据存储格式

1、数据是按照每一行的数据进行存储

2、根据x、y的pixel来确定

3、如果0<x<=32,则用4个bytes来存储,32<x<=64,则用8bytes来存储,以此类推a<=b*8

4、多余的bytes则为浪费的空间,例如x=33的bmp,要用8bytes来存储每行的信息,但是每行只用到5bytes,所以x的范围在很大程度上决定了bmp文件的大小

 

通用算法

1、以二进制文件打开文件

2、按照BMP文件信息和BMP图片信息开始读取,这里的长度是固定的

3、根据BMP文件信息中的数据偏移量得到,数据段的开始位置

4、根据BMP图片信息中x的pixel来取得每行需要读取的bytes

5、用一个数组来存储bmp数据信息BMPINFO[x][y]

6、每byte都为8个pixel的信息,多余部分不处理

 

 

一、这是我自己以16x16单色bmp位图格式保存的文件,用uedit打开学习它的数据格式:   
          00   01   02   03   04  05   06   07   08   09   0a   0b   0c  0d   0e   0f       
  ---------------------------------------------------   
  00h:   42   4d   7e   00   00   00   00  00   00   00   3e   00   00   00   28  00   
  10h:   00   00   20   00   00   00  20   00   00   00   01   00   01   00  00   00   
  20h:   00   00   80   00   00   00  c4   0e   00   00   c4   0e    00  00  00   00   
  30h:   00   00   00   00   00   00  00   00   00   00   ff    ff    ff    00   ff   ff   
  40h:   00   00   ff    ff    00   00   ff    ff    00   00   ff    ff    00   00   ff   ff   
  50h:   00   00   ff    ff    00   00   ff    ff    00   00   ff    ff    00   00   ff   ff   
  60h:   00   00   ff    ff    00   00   ff    ff    00   00   ff    ff    00   00   ff   ff   
  70h:   00   00   ff    ff    00   00   ff    ff    00   00   ff    ff    00   00   
    
  二、问题:   
  1,按照格式,偏移0ah处的值3eh,是图像数据的偏移位置,照此去读图像数据:  
  2,按理16x16的单色位图应该有16*16/8=32字节的图像数据,但这个文件有64个字节,   
      这是个全白图片,应该每行对应两字节:ffh    ffh,然实际是:ffh   ffh  00h   00h。   
  3,发现32x32尺寸的图片是对的:每行对应四个字节,ffh   ffh   ffh   ffh(全白);   
      然而,48x48尺寸的图片有不对了:ffh   ffh   ffh   ffh   ffh   ffh  00h   00h,后面又   
      "补"了两个字节的00(与16x16的类似),似乎要补足4的倍数?   
    
  三、bmp文件头格式:   
  -----------------------------------------------------------------------  
  偏移       bmp   文件头结构                    说明   
  -----------------------------------------------------------------------  
  00h           word   bftype                       "bm"   
  02h         dword   bfsize                        文件总长度   
  06h           word   bfreserved1              保留:固定为0  
  08h           word   bfreserved2              保留:固定为0  
  0eh         dword   bisize                        实际图像数据(离头)的偏移   
  12h         dword   biwidth                      图像宽度   
  16h         dword   biheight                    图像高度   
  1ah           word   biplanes                    色平面数   
  1ch           word   bibitcount                每个像素所占位元数  
  1eh         dword   bicompression          压缩方式,0表示无压缩   
  22h         dword   bisizeimage              图像(数据)的大小(字节)   
  26h         dword   bixpelspermeter      x   解析度(点数/米)   
  2ah         dword   biypelspermeter      y   解析度(点数/米)   
  2eh         dword   biclrused                  图像数据实际用到的颜色数  
  32h         dword   biclrimportant        调色板中有多少颜色数在显示时是重要的  
    
  四、调色板   
    1,紧接bmp   图像数据头结构之后的是调色板数据,每个颜色占用   4   个字节。

    2,调色板颜色数量是由bmp   图像数据开头结构中   biclrused  决定,如果   biclrused  为   0,则调色板中颜色的数量是   2bibitcount。      

    3,每个颜色的   4   个字节依序分别代表蓝色(blue)、绿色(green)、红色(red)、保留,每个颜色的分量最小为   0   ,最大是   255。      

    4,对于  bibitcount   >=   16   (全彩)   的图档而言,并不包含调色盘资板料。

调色板(color table)是单色、16色和256色图像文件所特有的,相对应的调色板大小是2、16和256,调色板以4字节为单位,每4个字节存放一个颜色值,图像的数据是指向调色板的索引。

      可以将调色板想象成一个数组,每个数组元素的大小为4字节,假设有一256色的BMP图像的调色板数据为:

 

调色板[0]=黑、调色板[1]=白、调色板[2]=红、调色板[3]=蓝…调色板[255]=黄

      图像数据01 00 02 FF表示调用调色板[1]、调色板[0]、调色板[2]和调色板[255]中的数据来显示图像颜色。

      在早期的计算机中,显卡相对比较落后,不一定能保证显示所有颜色,所以在调色板中的颜色数据应尽可能将图像中主要的颜色按顺序排列在前面,位图信息头的biClrImportant字段指出了有多少种颜色是重要的。

      每个调色板的大小为4字节,按蓝、绿、红存储一个颜色值。

      打开WINGDI.h文件,搜索"tagRGBTRIPLE"就可以定位到BMP文件的调色板的数据结构定义。

typedef struct tagRGBQUAD {
BYTE    rgbBlue;
BYTE    rgbGreen;
BYTE    rgbRed;
BYTE    rgbReserved;
} RGBQUAD;

      表4-1 列出了tagRGBTRIPLE中各字段的含义。

  表4-1  tagRGBTRIPLE结构 

字  段  名

大小(单位:字节)

描    述

rgbBlue

1

蓝色值

rgbGreen

1

绿色值

rgbRed

1

红色值

rgbReserved

1

保留,总为0

 

      如果图像是单色、16色和256色,则紧跟着调色板的是位图数据,位图数据是指向调色板的索引序号。

      如果位图是16位、24位和32位色,则图像文件中不保留调色板,即不存在调色板,图像的颜色直接在位图数据中给出。

      16位图像使用2字节保存颜色值,常见有两种格式:5位红5位绿5位蓝和5位红6位绿5位蓝,即555格式和565格式。555格式只使用了15位,最后一位保留,设为0。

      24位图像使用3字节保存颜色值,每一个字节代表一种颜色,按红、绿、蓝排列。

      32位图像使用4字节保存颜色值,每一个字节代表一种颜色,除了原来的红、绿、蓝,还有Alpha通道,即透明色。

      如果图像带有调色板,则位图数据可以根据需要选择压缩与不压缩,如果选择压缩,则根据BMP图像是16色或256色,采用RLE4或RLE8压缩算法压缩。 

     1:单色图,调色板中含有两种颜色,也就是我们通常说的黑白图片
     4:16色图
     8:256色图,通常说的灰度图
    16:64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个位表示一个RGB分量
    24:16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量
    32:4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式


 黑白:文件头(14字节)+信息头(40字节)+2个调色板(共8字节)+Height(图像高度)* (Width+8-Width%8)/8    我个人认为是当宽度除32除不尽时,宽度再加上一个32-宽度除以32后的余数,如宽度是300,真正计算宽度时用(300+32-12=320,注:12=300%32)
一行单色位图数据的存储格式规定: 每一扫描行的字节数必需是 4 的整倍数,当不够 4 的整数倍时,需要加 0 补齐
一个像素占1bit

     16色:文件头(14字节)+信息头(40字节)+16个调色板(共64字节)+Height(图像高度 ) * width或者(Width+4-Width%4)/2   宽度计算也错着,width/8能除尽不填充,如果除不尽它是对的(Width+8-Width%8)/2
一个像素占2bit
   
     256色:文件头(14字节)+信息头(40字节)+256个调色板(共1024字节)+Height(图像高度)* width或者(Width+4-Width%4)   宽度计算也错着,width/4能除尽不填充,如果除不尽它是对的(Width+4-Width%4)/2
一个像素占8bit
54+200*200+1024=41078字节 这是对的
54+100*300+1024=31078是对的
     16位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*2 (由于每个像素由两个字节表示) 先看width*2再去除以4,除尽H*W*2,除不尽H*(W*2+4-2*W%4)
一个像素占16bit
     24位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*3 (由于每个像素由三个字节表示), 先看width*3再去除以4,除尽H*W*3,除不尽H*(W*3+4-3*W%4)
一个像素占24bit
对于24位色的位图,如果它的宽度为1像素,那么除了每一行的数据(蓝、绿、红)需要占3字节外,还会填充1字节;而如果宽为2像素,则需要2字节的填充;宽为3像素时,需要3字节填充;宽为4像素时则不需要填充。

注:BMP文件的结构一般由14个字节的BITMAPFILEHEADER,40个字节的BITMAPINFOHEADER ,调色板组成、数据组成。16位(不含)以下的位图需要调色板索引得到像素RGB分量,调色板为一组RGBQUAD的结构体组成,其中,单色图有2个RGBQUAD,4位色有16个,8位色有256个。

RGB的表,即是我们常说的调色板(Palette),另一种叫法是颜色查找表LUT(LookUpTable),似乎更确切一些。
有一种图,它的颜色数高达256*256*256种,也就是说包含我们上述提到的R,G,B颜色表示方法中所有的颜色,这种图叫做真彩色图(TrueColor)。真彩色图并不是说一幅图包含了所有的颜色,而是说它具有显示所有颜色的能力,即最多可以包含所有的颜色。表示真彩色图时,每个象素直接用R,G,B三个分量字节表示,而不采用调色板技术,原因很明显:如果用调色板,表示一个象素也要用24位,这是因为每种颜色的索引要用24位(因为总共有2的24次方种颜色,即调色板有2的24次方行),和直接用R,G,B三个分量表示用的字节数一样,不但没有任何便宜,还要加上一个256*256*256*3个字节的大调色板。所以真彩色图直接用R,G,B三个分量表示,它又叫做24位色图。

+++++++++++++++++++++++++++++++++++++++++++

位图格式 BMP是bitmap的缩写形式,bitmap顾名思义,就是位图也即Windows位图。它一般由4部分组成:文件头信息块、图像描述信息块、颜色表(在真彩色模式无颜色表)和图像数据区组成。在系统中以BMP为扩展名保存。  

打开Windows的画图程序,在保存图像时,可以看到三个选项:2色位图(黑白)、16色位图、256色位图和24位位图。 

现在讲解BMP的4个组成部分:

1.文件头信息块

0000-0001 :文件标识,为字母ASCII码“BM”。

0002-0005 :文件大小。

0006-0009 :保留,每字节以“00”填写。

000A-000D :记录图像数据区的起始位置。各字节的信息含义依次为:文件头信息块大小,图像描述信息块的大小,图像颜色表的大小,保留(为01)。

2.图像描述信息块

000E-0011:图像描述信息块的大小,常为28H。

0012-0015:图像宽度。

0016-0019:图像高度。

001A-001B:图像的plane总数(恒为1)。

001C-001D:记录像素的位数,很重要的数值,图像的颜色数由该值决定。

001E-0021:数据压缩方式(数值位0:不压缩;1:8位压缩;2:4位压缩)。

0022-0025:图像区数据的大小。

0026-0029:水平每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

002A-002D:垂直每米有多少像素,在设备无关位图(.DIB)中,每字节以00H填写。

002E-0031:此图像所用的颜色数,如值为0,表示所有颜色一样重要。

3.颜色表 

颜色表的大小根据所使用的颜色模式而定:2色图像为8字节;16色图像位64字节;256色图像为1024字节。其中,每4字节表示一种颜色,并以B(蓝色)、G(绿色)、R(红色)、alpha(32位位图的透明度值,一般不需要)。即首先4字节表示颜色号0的颜色,接下来表示颜色号1的颜色,依此类推。

4.图像数据区   

颜色表接下来位是位图文件的图像数据区,在此部分记录着每点像素对应的颜色号,其记录方式也随颜色模式而定,既2色图像每点占1位;16色图像每点占4位;256色图像每点占8位;真彩色图像每点占24位。所以,整个数据区的大小也会随之变化。究其规律而言,可的出如下计算公式:图像数据信息大小=(图像宽度*图像高度*记录像素的位数)/8。 然而,未压缩的图像信息区的大小。除了真彩色模式外,其余的均大于或等于数据信息的大小。这是为什么呢?原因有两个:  

1.        BMP文件记录一行图像是以字节为单位的。因此,就不存在一个字节中的数据位信息表示的点在不同的两行中。也就是说,设显示模式位16色,在每个字节分配两个点信息时,如果图像的宽度位奇数,那么最后一个像素点的信息将独占一个字节,这个字节的后4位将没有意义。接下来的一个字节将开始记录下一行的信息。 

2.        为了显示的方便,除了真彩色外,其他的每中颜色模式的行字节数要用数据“00”补齐为4的整数倍。如果显示模式为16色,当图像宽为19时,存储时每行则要补充4-(19/2+1)%4=2个字节(加1是因为里面有一个像素点要独占了一字节)。如果显示模式为256色,当图像宽为19时,每行也要补充4-19%4=1个字节。

bmp文件大体上分成四个部分。

位图文件头BITMAPFILEHEADER 、位图信息头BITMAPINFOHEADER 、调色板Palette 、实际的位图数据ImageDate

第一部分为位图文件头BITMAPFILEHEADER,是一个结构,其定义如下:

typedef unsigned char BYTE

typedef unsigned short WORD

typedef unsigned long DWORD

typedef struct tagBITMAPFILEHEADER {

WORD bfType; //类型名,必须是0x424D,即字符串“BM”,

DWORD bfSize; //文件大小

WORD bfReserved1; //保留字,不考虑

WORD bfReserved2; //保留字,同上

DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和

} BITMAPFILEHEADER;

第二部分为位图信息头BITMAPINFOHEADER,也是一个结构,其定义如下:

typedef struct tagBITMAPINFOHEADER{

DWORD biSize; //指定此结构体的长度,为40

LONG biWidth; //位图宽

LONG biHeight; //位图高

WORD biPlanes; //平面数,为1

WORD biBitCount //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32

DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩

DWORD biSizeImage; //实际位图数据占用的字节数

LONG biXPelsPerMeter; //X方向分辨率

LONG biYPelsPerMeter; //Y方向分辨率

DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)

DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的

} BITMAPINFOHEADER;

第三部分为调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。

typedef struct tagRGBQUAD {

BYTE rgbBlue; //该颜色的蓝色分量

BYTE rgbGreen; //该颜色的绿色分量

BYTE rgbRed; //该颜色的红色分量

BYTE rgbReserved; //保留值

} RGBQUAD;

第四部分就是实际的图象数据了。对于用到调色板的位图,图象数据就是该象素颜在调色板中的索引值。对于真彩色图,图象数据就是实际的R、G、B值。对于2色位图,用1位就可以表示该象素的颜色(一般0表示黑,1表示白),所以一个字节可以表示8个象素。对于16色位图,用4位可以表示一个象素的颜色,所以一个字节可以表示2个象素。对于256色位图,一个字节刚好可以表示1个象素。对于真彩色图,三个字节才能表示1个象素。要注意两点: (1) 每一行的字节数必须是4的整倍数,如果不是,则需要补齐。 (2) 一般来说,.bMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个象素,然后是左边第二个象素……接下来是倒数第二行左边第一个象素,左边第二个象素……依次类推 ,最后得到的是最上面一行的最右一个象素。

16色系统调色板:

0 = RGB( 0, 0, 0) = 0x00000000;

1 = RGB(128, 0, 0) = 0x00000080;

2 = RGB( 0,128, 0) = 0x00008000;

3 = RGB(128,128, 0) = 0x00008080;

4 = RGB( 0, 0,128) = 0x00800000;

5 = RGB(128, 0,128) = 0x00800080;

6 = RGB( 0,128,128) = 0x00808000;

7 = RGB(128,128,128) = 0x00808080;

8 = RGB(192,192,192) = 0x00c0c0c0;

9 = RGB(255, 0, 0) = 0x000000ff;

10 = RGB( 0,255, 0) = 0x0000ff00;

11 = RGB(255,255, 0) = 0x0000ffff;

12 = RGB( 0, 0,255) = 0x00ff0000;

13 = RGB(255, 0,255) = 0x00ff00ff;

14 = RGB( 0,255,255) = 0x00ffff00;

15 = RGB(255,255,255) = 0x00ffffff;

图像数据起始:000A-000D

图像数据大小:0022-0025

图像信息大小:000E-0011

图像宽度:0012-0015

图像高度:0016-0019

*********************************************************************************************************************

*********************************************************************************************************************

BMP文件格式,又称为Bitmap位图)或是DIB(Device-Independent Device,设备无关位图),是Windows系统中广泛使用的图像文件格式。由于它可以不作任何变换地保存图像像素域的数据,因此成为我们取得RAW数据的重要来源。Windows图形用户界面(graphical user interfaces)也在它的内建图像子系统GDI中对BMP格式提供了支持。

下面以Notepad++为分析工具,结合Windows的位图数据结构BMP文件格式进行一个深度的剖析。

BMP文件的数据按照从文件头开始的先后顺序分为四个部分:

Ø         bmp文件头(bmp file header)提供文件的格式、大小等信息

Ø         位图信息头(bitmap information)提供图像数据的尺寸、位平面数、压缩方式、颜色索引等信息

Ø         调色板(color palette)可选,如使用索引来表示图像,调色板就是索引与其对应的颜色的映射表

Ø         位图数据(bitmap data)就是图像数据啦^_^

下面结合Windows结构体的定义,通过一个表来分析这四个部分。

我们一般见到的图像以24位图像为主,即RGB三种颜色各用8bit来表示,这样的图像我们称为真彩色,这种情况下是不需要调色板的,也就是所位图信息头后面紧跟的就是位图数据了。因此,我们常常见到有这样一种说法:位图文件从文件头开始偏移54个字节就是位图数据了,这其实说的是2432位图的情况。这也就解释了我们按照这种程序写出来的程序为什么对某些位图文件没用了。

  下面针对一幅特定的图像进行分析,来看看在位图文件中这四个数据段的排布以及组成。

 我们使用的图像显示如下:

                 

   这是一幅16位的位图文件,因此它是含有调色板的。

   在拉出图像数据进行分析之前,我们首先进行几个约定:

   1. BMP文件中,如果一个数据需要用几个字节来表示的话,那么该数据的存放字节顺序为“低地址村存放低位数据,高地址存放高位数据”。如数据0x1756在内存中的存储顺序为:

     

                                  

这种存储方式称为小端方式(little endian) , 与之相反的是大端方式(big endian)。对两者的使用情况有兴趣的可以深究一下,其中还是有学问的。

2.  以下所有分析均以字节为序号单位进行。

   下面我们对从文件中拉出来的数据进行剖析:

   

一、bmp文件头
Windowsbmp文件头定义了如下结构体:

  

typedef  struct tagBITMAPFILEHEADER 
{  
UINT16 bfType;    
DWORD bfSize; 
UINT16 bfReserved1; 
UINT16 bfReserved2; 
DWORD bfOffBits;
} BITMAPFILEHEADER; 

 

其中:

   

 对照文件数据我们看到:

1-2 424dh = 'BM',表示这是Windows支持的位图格式。有很多声称开头两个字节必须为'BM'才是位图文件,从上表来看应为开头两个字节必须为'BM'才是Windows位图文件。

3-5 00010436h = 66614 B = 65.05 kB,通过查询文件属性发现一致。

6-9 :这是两个保留段,为0

A-D00000436h = 1078。即从文件头到位图数据需偏移1078字节。我们稍后将验证这个数据。

共有14个字节。

二、位图信息头
同样地,Windows为位图信息头定义了如下结构体:

  

代码
typedef  struct tagBITMAPINFOHEADER
 {
DWORD biSize; 
LONG biWidth; 
LONG biHeight; 
WORD biPlanes; 
WORD biBitCount; 
DWORD biCompression; 
DWORD biSizeImage; 
LONG biXPelsPerMeter; 
LONG biYPelsPerMeter; 
DWORD biClrUsed; 
DWORD biClrImportant;
} BITMAPINFOHEADER;
 

 

对照数据文件: 

0E-1100000028h = 40,这就是说我这个位图信息头的大小为40个字节。前面我们已经说过位图信息头一般有40个字节,既然是这样,为什么这里还要给一个字段来说明呢?这里涉及到一些历史,其实位图信息头原本有很多大小的版本的。我们看一下下表:

                

    出于兼容性的考虑,大多数应用使用了旧版的位图信息头来保存文件。而 OS/2 已经过时了,因此现在最常用的格式就仅有V3 header了。因此,我们在前面说位图信息头的大小为40字节。

12-1500000100h = 256,图像宽为255像素,与文件属性一致。

16-1900000100h = 256,图像高为255像素,与文件属性一致。这是一个正数,说明图像数据是从图像左下角到右上角排列的。

1A-1B0001h, 该值总为1

1C-1D0008h = 8, 表示每个像素占8个比特,即该图像共有256种颜色。

1E-2100000000hBI_RGB 说明本图像不压缩。

22-2500000000h,图像的大小,因为使用BI_RGB,所以设置为0

26-2900000000h,水平分辨率,缺省。

2A-2D00000000h,垂直分辨率,缺省。

2E-3100000100h = 256,说明本位图实际使用的颜色索引数为256,与1C-ID得到的结论一致。

32-3500000100h = 256,说明本位图重要的颜色索引数为256,与前面得到的结论一致。

 

三、调色板
下面的数据就是调色板了。前面也已经提过,调色板其实是一张映射表,标识颜色索引号与其代表的颜色的对应关系。它在文件中的布局就像一个二维数组palette[N][4],其中N表示总的颜色索引数,每行的四个元素分别表示该索引对应的BGRAlpha的值,每个分量占一个字节。如不设透明通道时,Alpha0。因为前面知道,本图有256个颜色索引,因此N = 256。索引号就是所在行的行号,对应的颜色就是所在行的四个元素。这里截取一些数据来说明:

                           

 

索引:(蓝,绿,红,Alpha)

0号:(fefafd00)

1号:(fdf3fc00)

2号:(f4f3fc00)

3号:(fcf2f400)

4号:(f6f2f200)

                                                           5号:(fbf9f600) 等等。

 一共有256种颜色,每个颜色占用4个字节,就是一共1024个字节,再加上前面的文件信息头和位图信息头的54个字节加起来一共是1078个字节。也就是说在位图数据出现之前一共有1078个字节,与我们在文件信息头得到的信息:文件头到文图数据区的偏移为1078个字节一致!

四、位图数据

下面就是位图数据了,每个像素占一个字节,取得这个字节后,以该字节为索引查询相应的颜色,并显示到相应的显示设备上就可以了。

注意:由于位图信息头中的图像高度是正数,所以位图数据在文件中的排列顺序是从左下角到右上角,以行为主序排列的。

        

也即我们见到的第一个像素60是图像最左下角的数据,第二个人像素60为图像最后一行第二列的数据,…一直到最后一行的最后一列数据,后面紧接的是倒数第二行的第一列的数据,依此类推。

 如果图像是24位或是32位数据的位图的话,位图数据区就不是索引而是实际的像素值了。下面说明一下,此时位图数据区的每个像素的RGB颜色阵列排布:

24RGB按照BGR的顺序来存储每个像素的各颜色通道的值,一个像素的所有颜色分量值都存完后才存下一个下一个像素,不进行交织存储。

32位数据按照BGRA的顺序存储,其余与24位位图的方式一样。

像素的排布规则与前述一致。

对齐规则

讲完了像素的排列规则以及各像素的颜色分量的排列规则,最后我们谈谈数据的对齐规则。我们知道Windows默认的扫描的最小单位是4字节,如果数据对齐满足这个值的话对于数据的获取速度等都是有很大的增益的。因此,BMP图像顺应了这个要求,要求每行的数据的长度必须是4的倍数,如果不够需要进行比特填充(以0填充),这样可以达到按行的快速存取。这时,位图数据区的大小就未必是 图片宽×每像素字节数×图片高 能表示的了,因为每行可能还需要进行比特填充。

填充后的每行的字节数为:

     ,其中BPPBits Per Pixel)为每像素的比特数。

在程序中,我们可以表示为:

int iLineByteCnt = (((m_iImageWidth * m_iBitsPerPixel) + 31) >> 5) << 2;

这样,位图数据区的大小为:

  m_iImageDataSize = iLineByteCnt * m_iImageHeight;

我们在扫描完一行数据后,也可能接下来的数据并不是下一行的数据,可能需要跳过一段填充数据:

 skip = 4 - ((m_iImageWidth * m_iBitsPerPixel)>>3) & 3;

五、拾遗

至此,我们通过分析一个具体的位图文件例子详细地剖析了位图文件的组成。需要注意的是:我们讲的主要是PC机上的位图文件的构成,对于嵌入式平台,可能在调色板数据段与PC机的不同。如在嵌入式平台上常见的16r5g6b5位图实际上采用的掩模的方式而不是索引的方式来表示图像。此时,在调色板数据段共有四个部分,每个部分为四个字节,实际表示的是彩色版规范。即:

 第一个部分是红色分量的掩模

 第二个部分是绿色分量的掩模

 第三个部分是蓝色分量的掩模

 第四个部分是Alpha分量的掩模(缺省为0

典型的调色板规范在文件中的顺序为为:

 00F8 0000 E007 0000 1F00 0000 0000 0000

其中

    00F8 0000FB00h=1111100000000000(二进制),是蓝红分量的掩码。 
  E007 0000 07E0h=0000011111100000(二进制),是绿色分量的掩码。 
   1F00 0000001Fh=0000000000011111(二进制),是蓝色分量的掩码。 
    0000 0000设置为0

将掩码跟像素值进行运算再进行移位操作就可以得到各色分量值。看看掩码,就可以明白事实上在每个像素值的两个字节16位中,按从高到低取565位分别就是rgb分量值。取出分量值后把rgb值分别乘以848就可以补齐每个分量为一个字节,再把这三个字节按BGR组合,放入存储器,就可以转换为24位标准BMP格式了。

这样我们假设在位图数据区有一个像素的数据在文件中表示为02 F1。这个数据实际上应为F102

 r = (F102 AND F800) >> 8 = F0h = 240

 g= (F102 AND 07E0>> 3 = 20h = 32 
  b=(F102 AND 001F) << 3 = 10h =16

至此我们就可以显示了。(本文结束)

+++++++++++++++++++++++++++++++++++++++++++++++

bmp位图格式



位图文件是分成4部分的。第一部分是位图文件头,它包括位图文件类型,位图的大小和位图数据离文件头的偏移量。接下去的是位图信息头,它包括了位图的许多信息,比如位图的宽度,高度和位图使用的颜色数。再后面是颜色表,它可能包含了2个或更多的RGBQUAD结构。最后面是位图图像的数据。

一、位图结构如下:
 一、BMP文件结构

1. BMP文件组成
       BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。

2. BMP文件头
       BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
      其结构定义如下:

typedef struct tagBITMAPFILEHEADER 

{
          WORD bfType; // 位图文件的类型,必须为BM
          DWORD bfSize; // 位图文件的大小,以字节为单位
          WORD bfReserved1; // 位图文件保留字,必须为0
          WORD bfReserved2; // 位图文件保留字,必须为0
          DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
           // 文件头的偏移量表示,以字节为单位

  } BITMAPFILEHEADER;

3. 位图信息头

BMP位图信息头数据用于说明位图的尺寸等信息。

其结构定义如下:

typedef struct tagBITMAPINFOHEADER

{

     DWORD biSize; // 本结构所占用字节数

     LONG biWidth; // 位图的宽度,以像素为单位

     LONG biHeight; // 位图的高度,以像素为单位

     WORD biPlanes; // 目标设备的级别,必须为1

     WORD biBitCount// 每个像素所需的位数,必须是1(双色),

        // 4(16色),8(256色)或24(真彩色)之一

     DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),

        // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一

     DWORD biSizeImage; // 位图的大小,以字节为单位

     LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数

     LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数

     DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数

     DWORD biClrImportant;// 位图显示过程中重要的颜色数

} 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填充,一个扫描行所占的字节数计算方法:

 DataSizePerLine= (biWidth* biBitCount+31)/8;

 一个扫描行所占的字节数 DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数位图数据的大小(不压缩情况下):

 DataSize= DataSizePerLine* biHeight;
随便说一句,位图是设备无关图象,所以它的文件的扩展名就bmp

二、黑白BMP位图行补位和位图文件大小计

BMP图像行像素数必须是4的倍数,不足时将多余位会用0填充,毫无疑问对于任何颜色数BMP位图,这个条件都是成立的。因此各种颜色位数的BMP图像文件容量的计算公式总结如下:

     黑白:文件头(14字节)+信息头(40字节)+2个调色板(共8字节)+Height(图像高度)*(Width+8-Width%8)/8    

     16色:文件头(14字节)+信息头(40字节)+16个调色板(共64字节)+Height(图像高度)*(Width+4-Width%4)/2    

     256色:文件头(14字节)+信息头(40字节)+256个调色板(共1024字节)+Height(图像高度)*(Width+4-Width%4)

     24位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*3 (由于每个像素由三个字节表示)




    最近正在着手开发一个图片库,也就是实现对常见图片格式的度写操作。作为总结与积累,我会把这些图片格式以及加载的实现写在我的Blog上。

说到图片,位图(Bitmap)当然是最简单的,它Windows显示图片的基本格式,其文件扩展名为*.BMP。在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图个时候才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的(Flash中使用了矢量图,是按相同颜色区域存储的)。

一、下面我们来看看位图文件(*.BMP)的格式。

位图文件主要分为如下3个部分:

块名称

对应Windows结构体定义

大小(Byte

文件信息头

BITMAPFILEHEADER

14

位图信息头

BITMAPINFOHEADER

40

RGB颜色阵列

BYTE*

由图像长宽尺寸决定

1、   文件信息头BITMAPFILEHEADER

结构体定义如下:

typedef struct tagBITMAPFILEHEADER { /* bmfh */

UINT bfType;  
DWORD bfSize; 
UINT bfReserved1; 
UINT bfReserved2; 
DWORD bfOffBits;

} BITMAPFILEHEADER;

其中:

bfType

说明文件的类型,该值必需是0x4D42,也就是字符'BM'。

bfSize

说明该位图文件的大小,用字节为单位

bfReserved1

保留,必须设置为0

bfReserved2

保留,必须设置为0

bfOffBits

说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。

2、位图信息头BITMAPINFOHEADER

结构体定义如下:

typedef struct tagBITMAPINFOHEADER { /* bmih */

DWORD biSize; 
LONG biWidth; 
LONG biHeight; 
WORD biPlanes; 
WORD biBitCount; 
DWORD biCompression; 
DWORD biSizeImage; 
LONG biXPelsPerMeter; 
LONG biYPelsPerMeter; 
DWORD biClrUsed; 
DWORD biClrImportant;

} BITMAPINFOHEADER;

其中:

biSize

说明BITMAPINFOHEADER结构所需要的字数。

biWidth

说明图象的宽度,以象素为单位。

biHeight

说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。

biPlanes

为目标设备说明位面数,其值将总是被设为1。

biBitCount

说明比特数/象素,其值为1、4、8、16、24、或32。但是由于我们平时用到的图像绝大部分是24位和32位的,所以我们讨论这两类图像。

biCompression

说明图象数据压缩的类型,同样我们只讨论没有压缩的类型:BI_RGB

biSizeImage

说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0

biXPelsPerMeter

说明水平分辨率,用象素/米表示。

biYPelsPerMeter

说明垂直分辨率,用象素/米表示。

biClrUsed

说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。

biClrImportant

说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。

3、RGB颜色阵列

有关RGB三色空间我想大家都很熟悉,这里我想说的是在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位的RGB位图像素数据格式是:

蓝色B

绿色G

红色R

对于32位的RGB位图像素数据格式是:

蓝色B

绿色G

红色R

透明通道A

透明通道也称Alpha通道,该值是该像素点的透明属性,取值在0(全透明)到255(不透明)之间。对于24位的图像来说,因为没有Alpha通道,故整个图像都不透明。

二、搞清了文件格式,下一步我们要实现加载。

            加载文件的目的是要得到图片属性,以及RGB数据,然后可以将其绘制在DC(GDI),或是生成纹理对象(3D:OpenGL/Direct3D)。这两种用途在数据处理上有点区别,我们主要按前一种用法讲,在和3D有不同的地方,我们再提出来。

1、加载文件头

            //Load the file header

            BITMAPFILEHEADER header;

            memset(&header, 0, sizeof(header));

            inf.read((char*)&header, sizeof(header));

            if(header.bfType != 0x4D42)

                        return false;

            这个很简单,没有什么好说的。

            2、加载位图信息头

            //Load the image information header

            BITMAPINFOHEADER infoheader;

            memset(&infoheader, 0, sizeof(infoheader));

            inf.read((char*)&infoheader, sizeof(infoheader));

            m_iImageWidth = infoheader.biWidth;

            m_iImageHeight = infoheader.biHeight;

            m_iBitsPerPixel = infoheader.biBitCount;

            这里我们得到了3各重要的图形属性:宽,高,以及每个像素颜色所占用的位数。

3、行对齐

由于Windows在进行行扫描的时候最小的单位为4个字节,所以当

图片宽 X 每个像素的字节数 = 4的整数倍

时要在每行的后面补上缺少的字节,以0填充(一般来说当图像宽度为2的幂时不需要对齐)。位图文件里的数据在写入的时候已经进行了行对齐,也就是说加载的时候不需要再做行对齐。但是这样一来图片数据的长度就不是:宽 X  X 每个像素的字节数  了,我们需要通过下面的方法计算正确的数据长度:

//Calculate the image data size

int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;

m_iImageDataSize = iLineByteCnt * m_iImageHeight;

4、加载图片数据

对于24位和32位的位图文件,位图数据的偏移量为sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是说现在我们可以直接读取图像数据了。

            if(m_pImageData) delete []m_pImageData;

            m_pImageData = new unsigned char[m_iImageDataSize];

            inf.read((char*)m_pImageData, m_iImageDataSize);

如果你足够细心,就会发现内存m_pImageData里的数据的确是BGR格式,可以用个纯蓝色或者是纯红色的图片测试一下。

5、绘制

好了,数据和属性我们都有了,现在就可以拿来随便用了,就和吃馒头一样,爱粘白糖粘白糖,爱粘红糖粘红糖。下面是我的GDI绘制代码,仅作参考。

void CImage::DrawImage(HDC hdc, int iLeft, int iTop, int iWidth, int iHeight)

{

            if(!hdc || m_pImageData == NULL)

                        return;

            BITMAPINFO bmi;

            memset(&bmi, 0, sizeof(bmi));

            bmi.bmiHeader.biSize = sizeof(BITMAPINFO);

            bmi.bmiHeader.biWidth = m_iImageWidth;

            bmi.bmiHeader.biHeight = m_iImageHeight;

            bmi.bmiHeader.biPlanes = 1;

            bmi.bmiHeader.biBitCount = m_iBitsPerPixel;

            bmi.bmiHeader.biCompression = BI_RGB;

            bmi.bmiHeader.biSizeImage = m_iImageDataSize;

            StretchDIBits(hdc, iLeft, iTop, iWidth, iHeight,

                                                0, 0, m_iImageWidth, m_iImageHeight,

                                                m_pImageData, &bmi, DIB_RGB_COLORS, SRCCOPY);

}

6、3D(OpenGL)的不同之处

如果你是想用刚才我们得到的数据生成纹理对象,那么你还要请出下面的问题。

首先,用来生成纹理的数据不需要对齐,也就是说不能在每行的后面加上对齐的字节。当然在OpenGL里要求纹理图片的尺寸为2的幂,所以这个问题实际上不存在;

其次,我们得到的图形数据格式是BGR(BGRA),所以在生成纹理的时候,需指定格式为GL_BGR_EXT(GL_BGRA_EXT);否则需要做BGR->RGB(BGRA->RGBA)的转化。


位图文件是分成4部分的。第一部分是位图文件头,它包括位图文件类型,位图的大小和位图数据离文件头的偏移量。接下去的是位图信息头,它包括了位图的许多信息,比如位图的宽度,高度和位图使用的颜色数。再后面是颜色表,它可能包含了2个或更多的RGBQUAD结构。最后面是位图图像的数据。

一、位图结构如下:
 一、BMP文件结构

1. BMP文件组成
       BMP文件由文件头、位图信息头、颜色信息和图形数据四部分组成。

2. BMP文件头
       BMP文件头数据结构含有BMP文件的类型、文件大小和位图起始位置等信息。
      其结构定义如下:

typedef struct tagBITMAPFILEHEADER 

{
          WORD bfType; // 位图文件的类型,必须为BM
          DWORD bfSize; // 位图文件的大小,以字节为单位
          WORD bfReserved1; // 位图文件保留字,必须为0
          WORD bfReserved2; // 位图文件保留字,必须为0
          DWORD bfOffBits; // 位图数据的起始位置,以相对于位图
           // 文件头的偏移量表示,以字节为单位

  } BITMAPFILEHEADER;

3. 位图信息头

BMP位图信息头数据用于说明位图的尺寸等信息。

其结构定义如下:

typedef struct tagBITMAPINFOHEADER

{

     DWORD biSize; // 本结构所占用字节数

     LONG biWidth; // 位图的宽度,以像素为单位

     LONG biHeight; // 位图的高度,以像素为单位

     WORD biPlanes; // 目标设备的级别,必须为1

     WORD biBitCount// 每个像素所需的位数,必须是1(双色),

        // 4(16色),8(256色)或24(真彩色)之一

     DWORD biCompression; // 位图压缩类型,必须是 0(不压缩),

        // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一

     DWORD biSizeImage; // 位图的大小,以字节为单位

     LONG biXPelsPerMeter; // 位图水平分辨率,每米像素数

     LONG biYPelsPerMeter; // 位图垂直分辨率,每米像素数

     DWORD biClrUsed;// 位图实际使用的颜色表中的颜色数

     DWORD biClrImportant;// 位图显示过程中重要的颜色数

} 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填充,一个扫描行所占的字节数计算方法:

 DataSizePerLine= (biWidth* biBitCount+31)/8;

 一个扫描行所占的字节数 DataSizePerLine= DataSizePerLine/4*4; // 字节数必须是4的倍数位图数据的大小(不压缩情况下):

 DataSize= DataSizePerLine* biHeight;
随便说一句,位图是设备无关图象,所以它的文件的扩展名就bmp

二、黑白BMP位图行补位和位图文件大小计

BMP图像行像素数必须是4的倍数,不足时将多余位会用0填充,毫无疑问对于任何颜色数BMP位图,这个条件都是成立的。因此各种颜色位数的BMP图像文件容量的计算公式总结如下:

     黑白:文件头(14字节)+信息头(40字节)+2个调色板(共8字节)+Height(图像高度)*(Width+8-Width%8)/8    

     16色:文件头(14字节)+信息头(40字节)+16个调色板(共64字节)+Height(图像高度)*(Width+4-Width%4)/2    

     256色:文件头(14字节)+信息头(40字节)+256个调色板(共1024字节)+Height(图像高度)*(Width+4-Width%4)

     24位色:文件头(14字节)+信息头(40字节)+Height(图像高度)*(Width+4-Width%4)*3 (由于每个像素由三个字节表示)




    最近正在着手开发一个图片库,也就是实现对常见图片格式的度写操作。作为总结与积累,我会把这些图片格式以及加载的实现写在我的Blog上。

说到图片,位图(Bitmap)当然是最简单的,它Windows显示图片的基本格式,其文件扩展名为*.BMP。在Windows下,任何各式的图片文件(包括视频播放)都要转化为位图个时候才能显示出来,各种格式的图片文件也都是在位图格式的基础上采用不同的压缩算法生成的(Flash中使用了矢量图,是按相同颜色区域存储的)。

一、下面我们来看看位图文件(*.BMP)的格式。

位图文件主要分为如下3个部分:

块名称

对应Windows结构体定义

大小(Byte

文件信息头

BITMAPFILEHEADER

14

位图信息头

BITMAPINFOHEADER

40

RGB颜色阵列

BYTE*

由图像长宽尺寸决定

1、   文件信息头BITMAPFILEHEADER

结构体定义如下:

typedef struct tagBITMAPFILEHEADER { /* bmfh */

UINT bfType;  
DWORD bfSize; 
UINT bfReserved1; 
UINT bfReserved2; 
DWORD bfOffBits;

} BITMAPFILEHEADER;

其中:

bfType

说明文件的类型,该值必需是0x4D42,也就是字符'BM'。

bfSize

说明该位图文件的大小,用字节为单位

bfReserved1

保留,必须设置为0

bfReserved2

保留,必须设置为0

bfOffBits

说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。

2、位图信息头BITMAPINFOHEADER

结构体定义如下:

typedef struct tagBITMAPINFOHEADER { /* bmih */

DWORD biSize; 
LONG biWidth; 
LONG biHeight; 
WORD biPlanes; 
WORD biBitCount; 
DWORD biCompression; 
DWORD biSizeImage; 
LONG biXPelsPerMeter; 
LONG biYPelsPerMeter; 
DWORD biClrUsed; 
DWORD biClrImportant;

} BITMAPINFOHEADER;

其中:

biSize

说明BITMAPINFOHEADER结构所需要的字数。

biWidth

说明图象的宽度,以象素为单位。

biHeight

说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。

biPlanes

为目标设备说明位面数,其值将总是被设为1。

biBitCount

说明比特数/象素,其值为1、4、8、16、24、或32。但是由于我们平时用到的图像绝大部分是24位和32位的,所以我们讨论这两类图像。

biCompression

说明图象数据压缩的类型,同样我们只讨论没有压缩的类型:BI_RGB

biSizeImage

说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0

biXPelsPerMeter

说明水平分辨率,用象素/米表示。

biYPelsPerMeter

说明垂直分辨率,用象素/米表示。

biClrUsed

说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)。

biClrImportant

说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。

3、RGB颜色阵列

有关RGB三色空间我想大家都很熟悉,这里我想说的是在Windows下,RGB颜色阵列存储的格式其实BGR。也就是说,对于24位的RGB位图像素数据格式是:

蓝色B

绿色G

红色R

对于32位的RGB位图像素数据格式是:

蓝色B

绿色G

红色R

透明通道A

透明通道也称Alpha通道,该值是该像素点的透明属性,取值在0(全透明)到255(不透明)之间。对于24位的图像来说,因为没有Alpha通道,故整个图像都不透明。

二、搞清了文件格式,下一步我们要实现加载。

            加载文件的目的是要得到图片属性,以及RGB数据,然后可以将其绘制在DC(GDI),或是生成纹理对象(3D:OpenGL/Direct3D)。这两种用途在数据处理上有点区别,我们主要按前一种用法讲,在和3D有不同的地方,我们再提出来。

1、加载文件头

            //Load the file header

            BITMAPFILEHEADER header;

            memset(&header, 0, sizeof(header));

            inf.read((char*)&header, sizeof(header));

            if(header.bfType != 0x4D42)

                        return false;

            这个很简单,没有什么好说的。

            2、加载位图信息头

            //Load the image information header

            BITMAPINFOHEADER infoheader;

            memset(&infoheader, 0, sizeof(infoheader));

            inf.read((char*)&infoheader, sizeof(infoheader));

            m_iImageWidth = infoheader.biWidth;

            m_iImageHeight = infoheader.biHeight;

            m_iBitsPerPixel = infoheader.biBitCount;

            这里我们得到了3各重要的图形属性:宽,高,以及每个像素颜色所占用的位数。

3、行对齐

由于Windows在进行行扫描的时候最小的单位为4个字节,所以当

图片宽 X 每个像素的字节数 = 4的整数倍

时要在每行的后面补上缺少的字节,以0填充(一般来说当图像宽度为2的幂时不需要对齐)。位图文件里的数据在写入的时候已经进行了行对齐,也就是说加载的时候不需要再做行对齐。但是这样一来图片数据的长度就不是:宽 X  X 每个像素的字节数  了,我们需要通过下面的方法计算正确的数据长度:

//Calculate the image data size

int iLineByteCnt = (((m_iImageWidth*m_iBitsPerPixel) + 31) >> 5) << 2;

m_iImageDataSize = iLineByteCnt * m_iImageHeight;

4、加载图片数据

对于24位和32位的位图文件,位图数据的偏移量为sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER),也就是说现在我们可以直接读取图像数据了。

            if(m_pImageData) delete []m_pImageData;

            m_pImageData = new unsigned char[m_iImageDataSize];

            inf.read((char*)m_pImageData, m_iImageDataSize);

如果你足够细心,就会发现内存m_pImageData里的数据的确是BGR格式,可以用个纯蓝色或者是纯红色的图片测试一下。

5、绘制

好了,数据和属性我们都有了,现在就可以拿来随便用了,就和吃馒头一样,爱粘白糖粘白糖,爱粘红糖粘红糖。下面是我的GDI绘制代码,仅作参考。

void CImage::DrawImage(HDC hdc, int iLeft, int iTop, int iWidth, int iHeight)

{

            if(!hdc || m_pImageData == NULL)

                        return;

            BITMAPINFO bmi;

            memset(&bmi, 0, sizeof(bmi));

            bmi.bmiHeader.biSize = sizeof(BITMAPINFO);

            bmi.bmiHeader.biWidth = m_iImageWidth;

            bmi.bmiHeader.biHeight = m_iImageHeight;

            bmi.bmiHeader.biPlanes = 1;

            bmi.bmiHeader.biBitCount = m_iBitsPerPixel;

            bmi.bmiHeader.biCompression = BI_RGB;

            bmi.bmiHeader.biSizeImage = m_iImageDataSize;

            StretchDIBits(hdc, iLeft, iTop, iWidth, iHeight,

                                                0, 0, m_iImageWidth, m_iImageHeight,

                                                m_pImageData, &bmi, DIB_RGB_COLORS, SRCCOPY);

}

6、3D(OpenGL)的不同之处

如果你是想用刚才我们得到的数据生成纹理对象,那么你还要请出下面的问题。

首先,用来生成纹理的数据不需要对齐,也就是说不能在每行的后面加上对齐的字节。当然在OpenGL里要求纹理图片的尺寸为2的幂,所以这个问题实际上不存在;

其次,我们得到的图形数据格式是BGR(BGRA),所以在生成纹理的时候,需指定格式为GL_BGR_EXT(GL_BGRA_EXT);否则需要做BGR->RGB(BGRA->RGBA)的转化。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值