很多人在转储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字节内容:
内容 | 长度 | 填写(注意还是要低在前) |
红色占位 | 4字节 | RGB565 要填:0x0000F800 |
绿色占位 | 4字节 | RGB565 要填:0x000007E0 |
蓝色占位 | 4字节 | RGB565 要填:0x0000001F |
预留 | 4字节 | 填00 |
注意:
由于头信息变成 70字节了,因此 偏移量/长度 地方的值也要随着改变
还有一个地方:000EH 这个地方要增加16,这里要填 0x38 !
ps:由此可见,如果是RGB555的话,这些地方也要改
可以看下实际51*51的头,用UE:
还有一个坑,就是bmp要4字节对齐,本来是很好理解的,但实际却是
“每一行都得4字节对齐,不足的用 00H 补齐”
举个例子:
如果图片是 51 * 51 格式: RGB565(一个像素点为2字节)
每行字节数=51*2=102字节,102不是4倍数,所以要补偿两个00,使之=104字节。
实际文件长度一共补偿了102字节。
下面给一个我创建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 }
原文地址:https://www.cnblogs.com/winafa/p/11652343.html