分类: C/C++
2008-12-15 12:17:04
bmp图片就是位图,是在几乎任何的操作平台上都支持的图片格式,今天我们就用bmp图片来做一个有趣的试验,不用任何的画图工具, 我们只用16进制编辑器,在里面输入16进制数,然后保存后缀为.bmp的文件,由此而构成一副bmp图片。虽然最后生成的bmp图片在现实生活中没有任何的用处, 但是在纯手工构建这个图片的过程中,你能深刻的体会到bmp图片是怎么构成的,这样对于我们做有关bmp图片的编程时也是大有裨益的,同时我们还能从中体会到计算机表示数据的思想。OK, let's begin:
为了减少我们输入和计算的麻烦,我将会构建一个比较简单的图片,16*16的图片,即宽16个像素,高16个像素的bmp图片,同时为了不涉及到调色板这种比较麻烦的问题,我们将构建24位色的图片,即用R,G,B三原色表示一个像素点的图片。
首先我们需要了解一下bmp图片的文件头的组成:
bmp图片的文件头由下面2个结构构成:
typedef
struct tagBITMAPFILEHEADER {
WORD
bfType; // 文件类型
DWORD
bfSize; // 文件大小
WORD
bfReserved1; // 保留值,为0
WORD
bfReserved2; // 保留值,为0
DWORD
bfOffBits; // 文件中数据的偏移
}
BITMAPFILEHEADER;
typedef
struct tagBITMAPINFO {
BITMAPINFOHEADER bmiHeader;
// bmp文件信息头
RGBQUAD bmiColors[1]; // 调色板数组
}
BITMAPINFO;
在BITMAPINFO结构中,根据bmp图片位数的不同(1bit, 2bit, 8bit, 16bit, 24bit,
32bit)RGQUARD这个数组的大小也不一样,而对于24位色以上的图片,这个结构大小为0,即没有调色板,它的后面的数值就代表了像素的颜色。所以,对于24位色的bmp图片,实际构成将会是下面这个样子:
BITMAPFILEHEADER |
BITMAPINFOHEADER |
DATA
(R, G, B) |
所以,对于我们的任务就是填充文件头BITMAPFILEHEADER和信息头BITMAPINFOHEADER以及数据区域。
接着,我们要做的事情就是,根据文件头每个字段的意义以及大小分别填充它们。为了让大家看的更加清楚,我将会以表格的形式来填充每个字段。
大小 |
字段 |
值和意义 |
WORD |
bfType |
文件类型,标识,必须为”BM”,即0x4d42 |
DWORD |
bfSize |
整个bmp文件的大小,包括2个头以及数据区域,计算方法如下: 16*16*3 因为我们要设计一个16×16的24色位图,所以整个数据区域的大小为:宽度×高度×每个像素的大小,因为每个像素是用RGB三原色表示的,所以,每个像素所占大小为3个字节,即这个字段的大小为: sizeof(BITMAPFILEHEADER) = 14 + 即14+40+768=822个字节,即0x0336字节 |
WORD |
bfReserved1 |
保留值,必须为0 |
WORD |
bfReserved2 |
保留值,必须为0 |
DWORD |
bfOffBits |
数据到文件开始位置的偏移,即 即14+40=54,即 |
DWORD |
biSize |
本结构的大小,即sizeof(BITMAPINFOHEADER) = 40,即0x28 |
LONG |
biWidth |
位图的宽度,我们设为16,即0x10 |
LONG |
biHeight |
位图的高度,我们设为16,即0x10 |
WORD |
biPlanes |
固定值1,即0x01 |
WORD |
biBitCount |
多少位色的图片,这里为24,0x18 |
DWORD |
biCompression |
压缩方式,这里为BI_RGB= 0x00 |
DWORD |
biSizeImage |
这里无需关心,设置为0,详细意义查MSDN |
LONG |
biXPelsPerMeter |
这里无需关心,设置为0,详细意义查MSDN |
LONG |
biYPelsPerMeter |
这里无需关心,设置为0,详细意义查MSDN |
DWORD |
biClrUsed |
这里无需关心,设置为0,详细意义查MSDN |
DWORD |
biClrImportant |
这里无需关心,设置为0,详细意义查MSDN |
了解了每个字段的含义以及计算好了它们的值之后,下面的任务就是要在16进制编辑器里面把它写出来,这里,我将使用比较流行的WinHex。
打开WinHex,新建立一个文件,大小为822个字节,然后将文件保存为test.bmp,接着我们开始填充这个文件的每个字节。因为平时我们表示一个16进制数字的时候,高字节在前,低字节在后,而文件中正好相反,即低字节在前,而高字节在后,所以,当一个数字是0x4d42的时候,我们在编辑器里面填入42 4D ,所有的数字都以此类推,不再说明。
我们先来填充BITMAPFILEHEADER结构的每个字段的值,如下:
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 42 4D 36 03 00 00 00 00 00 00 36
接着,我们来填充BITMAPINFOHEADER结构的每个字段的值,要接着上面的开始填:
0 1 2 3 4 5 6 7 8 9 A B C D E F
00000000 42 4D 36 03 00 00 00 00 00 00 36 00 00 00 28 00
00000010 00 00 10 00 00 00 10 00 00 00 01 00 18 00 00 00
00000020 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000030 00 00 00 00 00 00 FF 00 00 FF 00 00 FF 00 00 FF
00000040
.......
00000330 00 FF FF 00 FF FF
上面的红色部分表示BITMAPFILEHEADER结构填充的内容,蓝色部分表示BITMAPINFOHEADER结构填充的内容,从0x0036开始,到0x0335结束,都是数据区域的内容,这里面可以随便填,填入内容的不同,显示的bmp图片也不一样,全部内容填充完毕,重新保存一下文件,然后用windows自带的图片浏览器就可以查看自己纯手写出来的bmp图片了。
需要说明的是,我们在计算图片数据大小的时候,一定是宽度×高度×3, 我刚开始做的时候,因为忘了×3, 结果图片总是无法正确显示,后来检查了多遍,才发现这个问题。对于文件头的数据,每个字节都必须填写正确,只要有一个字节出错,图片就可能无法正确显示。
手写的效率比较低,如果想知道用程序如何生成一幅bmp图片,请看下面的文章:
Author: thinker
QQ: 94483026
E-Mail:cnhnyu