很多人在转储bmp文件的时候,会出现各种各样的问题,特别是抓屏的时候,经常保存下来的图片 怪怪的,偏差很大!比如下面:
有时候,明明感觉自己是对的,但往往结果很让人抓狂。
这种情况一般是对bmp文件格式理解不对,或者没有透彻导致,当然至少是显示出来,所以大部分是对的,只是某些地方出错!
网上也有很多bmp文件格式,但都说得不够透彻,导致实际总要走些弯路。
bmp是常见图片格式,使用非常广泛。近期在处理ui库的时候,了解下bmp格式,也发现其中一些坑,记录下。
bmp格式很简单,网上搜索一堆,百科上也有介绍,这里就不重复介绍,
最重要就是文件头,主要是 前54字节,可以用UE看看 bmp:
注意:
1. 红色框里的内容,是重点
2. 表示数值的,都是低在前,包括后面的像素点。例如,RGB888 实际存放顺序是 BGR888
颜色:有几种标准,RGB是常见一种,也非常好理解,所以下面重点是RGB颜色标准。
由于4色,黑白色,16色,256色,色差失真太大,除了特殊场合,现在很少用了,因此,我也不怎么去尝试。
对于256色,其实存在画板的概念,可以提升图片效果,降低存储空间,有兴趣可以去了解下
重点说下:16位色 24位色
注意区别:16色与16位色,16色就是16种颜色,非常单调;16位色,就是用16bit 来表示颜色,有65536种颜色,比较丰富。
24位色就是24bit来表示颜色,那么有256*256*256种颜色,那么颜色更丰富,满足大部分的正常使用。
既然24位色这么好,为什么还有用16位色呢?甚至更低色?主要场合不一样,位数越少,表现效果当然越差了,但存储占空间就越少,意味着处理就更快,在显示要求并不是那么高的地方,还是很有市场的,另外颜色也受到显示对象限制,有年代的人应该知道以前屏幕是单色的,因此再多的色彩最终还是 0或1;后来出现彩色屏幕,但颜色也很少,就那么几种,但比单色效果好很多了;再后来出现 真彩色,才使得我们进入888的时代,当然以后更多,颜色也更绚丽。总之,颜色取决于你的设备/处理能力/硬件成本。
前面说到的 RGB888就是 24位色=8+8+8
那么16位色又是?RGB565=5+6+5,还有RGB555=5+5+5 ,一般RGB565比较多。
“在我们的计算机中图像是以RGB888格式显示图像的,24位图每个像素保存了32bit的数据,即RGB888+Alpha,Alpha就是半透明填充字节……” 但设备当中,我们经常用RGB565,因此两者之间是要转换的,转换算法,网上很多,也很简单,这里就不重复描述。
当然实际不会这么简单,我就碰到这么一个坑,通过坑,就好好理解BMP格式。
不知道大家有没有注意到bmp格式里的 001EH 这里的含义:
0 - 不压缩 (使用BI_RGB表示)
1 - RLE 8-使用8位RLE压缩方式(用BI_RLE8表示)
2 - RLE 4-使用4位RLE压缩方式(用BI_RLE4表示)
3 - Bitfields-位域存放方式(用BI_BITFIELDS表示) 一般用于bit12以及bit32,这里是 重点!重点!重点!重点!
因为目标文件是RGB565,16bit,所以 001E这里要填 3,注意不能填0!注意不能填0 !注意不能填0!
那么填了3是否就OK了呢?不是!因为16bit,还有RGB565 以及 RGB555两种标准,你还要告诉对方用哪种格式!不然系统就傻了!
那么怎么告诉系统呢?需要在 原先54字节 后面增加一些信息,主要增加16字节内容:
红色占位 RGB565 要填:0x0000F800
绿色占位RGB565 要填:0x000007E0
蓝色占位RGB565 要填:0x0000001F
由于改成70字节了,因此 偏移量/长度 地方的值也要随着改变
还有一个地方:000EH 这个地方要增加16,这里要填 0x38 !
由此可见,如果是RGB555的话,这些地方也要改
还有一个坑,就是bmp要4字节对齐,本来是很好理解的,但实际却是
“每一行都得4字节对齐,不足的用 00H 补齐”
举个例子:
如果图片是 51 * 51 格式: RGB565(一个像素点为2字节)
每行字节数=51*2=102字节,102不是4倍数,所以要补偿两个00,使之=104字节
下面给一个我创建bmp头函数,就可以实现自己的bmp头,由于平时就用到RGB888 跟 RGB565 两种,所以没考虑其他类型的,如果有兴趣可以完善以下代码,使之更加通用!
1 //pTitle 固定为70字节, 创建
2 //byBits 位数,比如16 24
3 //nWidth 图片宽度(多少像素点)
4 //nHeight 图片高度(多少像素点)
5 //nFileLen 图片总像素点 = nWidth * nHeight
6 void CreateBmpTitle(byte *pTitle,byte byBits,UINT nWidth,UINT nHeight,UINT nFileLen)
7 {
8 int nPos = 0;
9 memset(pTitle,0,70);
10
11 //每个像素占多少字节
12 byte bmpBytes = 2;
13 if(byBits <= 8)
14 {
15 bmpBytes = 1;
16 }
17 else if(byBits <= 16)
18 {
19 bmpBytes = 2;
20 }
21 else if(byBits <= 24)
22 {
23 bmpBytes = 3;
24 }
25 else
26 {
27 bmpBytes = 4;
28 }
29
30 //每一行都要凑齐4
31 UINT nLineLen = nWidth * bmpBytes;
32 if((nLineLen % 4) != 0)
33 {
34 UINT nAdd = (4 - (nLineLen % 4)) * nHeight;
35 nFileLen += nAdd;
36 }
37
38 pTitle[nPos++] = 'B';
39 pTitle[nPos++] = 'M';
40
41 //文件长度
42 SetUINT_L(nFileLen+70,pTitle,nPos);
43 nPos += 8;//预留4也跳过
44
45 //偏移量
46 SetUINT_L(70,pTitle,nPos);
47 nPos += 4;
48
49 //windows, 56
50 SetUINT_L(56,pTitle,nPos);
51 nPos += 4;
52
53 //宽度
54 SetUINT_L(nWidth,pTitle,nPos);
55 nPos += 4;
56 //高度
57 SetUINT_L(nHeight,pTitle,nPos);
58 nPos += 4;
59 //平面数=1
60 SetUSHORT_L(1,pTitle,nPos);
61 nPos += 2;
62
63 //bits 数
64 SetUSHORT_L((USHORT)byBits,pTitle,nPos);
65 nPos += 2;
66
67 if(byBits == 24)
68 {
69 nPos += 4;
70 }
71 else if((byBits == 16) || (byBits == 15) || (byBits == 32))
72 {
73 //BI_BITFIELDS
74 SetUINT_L(3,pTitle,nPos);
75 nPos += 4;
76 }
77
78 //内容长度,必须为4倍数
79 SetUINT_L(nFileLen,pTitle,nPos);
80 nPos += 4;
81
82 SetUINT_L(0xb12,pTitle,nPos);
83 nPos += 4;
84
85 SetUINT_L(0xb12,pTitle,nPos);
86 nPos += 4;
87
88 if(byBits == 16)
89 {
90 // 565 分别表示 RGB 所在bit位数
91 nPos = 54;
92
93 SetUINT_L(0xF800,pTitle,nPos);
94 nPos += 4;
95
96 SetUINT_L(0x07E0,pTitle,nPos);
97 nPos += 4;
98
99 SetUINT_L(0x001F,pTitle,nPos);
100 nPos += 4;
101 }
102
103
104 ///其他都是0
105 }
知识兔