Published by orzz.org(). (https://orzz.org/%e5%ae%8c%e6%95%b4%e7%9a%84%e4%bd%8d%e5%9b%be%e6%96%87%e4%bb%b6%e8%a7%a3%e6%9e%90/)
与上次简单的位图加载不同,这次是完整的位图格式解析.暂时没有考虑压缩格式的位图.
下面的内容难免有错误,各位如发现纰漏请及时指出.
- 1. 位图结构介绍
位图其实比较复杂,主要是由于各种原因导致的标准拓展和复杂化所致.
从磁盘上加载的BMP图片属于设备无关位图(DIB).通常来讲,DIB位图的基本结构如下:
其中,最上面的BITMAPFILEHEADER可以由后面的BITMAPINFO还原出来,没有BITMAPFILEHEADER的DIB被成为"压缩DIB".这里的压缩,指的是DIB的其余部分在连续的内存空间里紧挨着存放.
很多用于加载DIB的API,如LoadResource,LoadImage之类,它们返回的DIB句柄就是一个压缩的DIB.
- BITMAPFILEHEADER 位图文件头
BITMAPFILEHEADER 结构定义:
1 2 3 4 5 6 7 |
typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, FAR *LPBITMAPFILEHEADER, *PBITMAPFILEHEADER; |
bfType是BMP文件的签名.对于目前的BMP文件,此字段只可能是两个ASCII字符'B'和'M'.其他的字段内容表示的文件仅被用在OS/2系统中.在校验BMP文件时可以直接通过检查文件最开头的16位字符是否为0x4D42来判断此文件是否为BMP文件.
bfSize存储了整个BMP文件的大小.
bfOffBits是Pixel数据段相对文件最开始的偏移量.
- BITMAPINFO 位图信息
BITMAPINFO 结构定义:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader; RGBQUAD bmiColors[1]; } BITMAPINFO, FAR *LPBITMAPINFO, *PBITMAPINFO; 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, FAR *LPBITMAPINFOHEADER, *PBITMAPINFOHEADER; typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD; typedef RGBQUAD FAR* LPRGBQUAD; |
BITMAPINFOHEADER为位图信息头.
位图信息头的版本相当多,最初的版本是BITMAPCOREHEADER,仅用于OS/2,从Win3.1开始是BITMAPINFOHEADER,被称为位图信息结构版本3;在后面还有配合Win95与NT中新特征而形成的版本4:BITMAPV4HEADER;以及配合Win98和Win2K中新特征而形成的版本5:BITMAPV5HEADER.
一般最经常看到的是BITMAPINFOHEADER,我们这里只简要介绍BITMAPINFOHEADER.
biSize在这里必须等于sizeof(BITMAPINFOHEADER),这是用来判断信息头版本的标记.其实更高版本的信息头只是在低版本上做了一些拓展,依然可以用低版本的方式解析.
biWidth与biHeight是以像素为单位的图像宽度和高度(BITMAPCOREHEADER中它们的类型是WORD,后面的版本都是LONG).biHeight可能取负值.当biHeight<0时表示像素阵列的扫描线由上至下进行.
biPlanes总是等于1.在MSDN中有这样的描述:"Specifies the number of planes for the target device. This value must be set to 1".DIB只支持单个位平面的图像.
biBitCount,表示一个像素所需要的二进制位数.可能有以下取值:1;2;4;8;16;24;32.
biCompression,压缩格式.BI_RGB与BI_BITFIELDS表示未压缩的位图.16位与32位位图的此字段可以是BI_BITFIELDS,此时在位图信息头之后将加入3个DWORD表示如何从Pixel中提取RGB的屏蔽位.
biSizeImage是像素阵列的大小.BI_RGB的位图此字段可以是0.由于像素字节数组中每个扫描行的字节数必需是4的倍数,不足将会用0补齐,因此真正的像素阵列行大小计算起来有点绕.有个通用的公式可以解决这个问题:pitch = 4 * ( (biWidth * biBitCount + 31) / 32 );当然,还有很多其他的计算方法,这里就不一一列举了.最后,biSizeImage = pitch * biHeight;
biClrUsed表示颜色表的项数.
biClrImportant表示显示位图实际需要的颜色表项数.
- Pixel[][] 像素阵列
不同位数的位图,其像素阵列的存取都有所不同.
1,4,8位位图的像素阵列中存放的不是像素,而是此像素在颜色表中的索引.通过索引查找颜色表就可以得到这个像素点的颜色值.
16位位图是最为麻烦的.不仅位图数据分555与565(指RGB三个分量在每个16位像素中的排列方法)两种格式,位图类型也分BI_RGB与BI_BITFIELDS两种类型.并且最后得到的RGB数据还需要处理成合适的大小,因为16位中每个通道只有32或64级,但常规颜色是256级,因此需要按照色深进行转换.
24位位图比较简单,每个24位像素中RGB按888排列,即每个通道一个BYTE.
32位位图可以比24位位图多一个Alpha通道(但是不一定),因此可以显示半透明效果(RGBA).其他读取同24位位图一致.
- 2. 位图像素读取
下面给出各种不同位数位图像素阵列的读取方法.
首先是通用的数据预读取,这里定义一个宏来完成所有工作:
1 2 3 4 5 6 7 8 9 10 11 12 |
#pragma push_macro("PreDecode") #undef PreDecode #define PreDecode() int img_w = bmiInfo.bmiHeader.biWidth; int img_h = bmiInfo.bmiHeader.biHeight; int bit_c = bmiInfo.bmiHeader.biBitCount; int bit_s = bmhHead.bfOffBits; int pit_b = PitchBytes(img_w, bit_c); int pit_w = PitchWidth(pit_b); CGC gc; BYTE* temp = ExMem::Alloc<BYTE>(&gc, pit_b) //#define PreDecode |
下面是具体的像素读取算法.
- 1位位图(单色位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
PreDecode(); // 获取调色板 RGBQUAD colors[1 << 1] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++inx) { for(int n = 7; n >= 0 && x < img_w; --n, ++x, ++pos) { BYTE c = (temp[inx] >> (1 * n)) & 0x01; bmBuff[pos] = ExRGBA ( colors[c].rgbBlue, colors[c].rgbGreen, colors[c].rgbRed, (BYTE)~0 ); } } } |
- 4位位图(16色位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
PreDecode(); // 获取调色板 RGBQUAD colors[1 << 4] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++inx) { for(int n = 1; n >= 0 && x < img_w; --n, ++x, ++pos) { BYTE c = (temp[inx] >> (4 * n)) & 0x0F; bmBuff[pos] = ExRGBA ( colors[c].rgbBlue, colors[c].rgbGreen, colors[c].rgbRed, (BYTE)~0 ); } } } |
- 8位位图(256色位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
PreDecode(); // 获取调色板 RGBQUAD colors[1 << 8] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); for(int x = 0; x < img_w; ++x, ++pos) { bmBuff[pos] = ExRGBA ( colors[temp[x]].rgbBlue, colors[temp[x]].rgbGreen, colors[temp[x]].rgbRed, (BYTE)~0 ); } } |
- 16位位图(高彩位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
PreDecode(); // 获取掩码 uint32_t mask[3] = {0}; if (bmiInfo.bmiHeader.biCompression == BI_RGB) { mask[0] = 0x7C00; mask[1] = 0x03E0; mask[2] = 0x001F; } else // BI_BITFIELDS { pFile->Seek(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), IFileObject::begin); pFile->Read(mask, sizeof(mask), 1); } BYTE cnt_m[3] = { BitCount(mask[0]), BitCount(mask[1]), BitCount(mask[2]) }; // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 2) { uint16_t pixel = *(uint16_t*)(temp + inx); bmBuff[pos] = ExRGBA ( (pixel & mask[2]) << (8 - cnt_m[2]), ((pixel & mask[1]) >> cnt_m[2]) << (8 - cnt_m[1]), ((pixel & mask[0]) >> (cnt_m[1] + cnt_m[2])) << (8 - cnt_m[0]), (BYTE)~0 ); } } |
- 24位位图(真彩位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
PreDecode(); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 3) { bmBuff[pos] = ExRGBA ( temp[inx], temp[inx + 1], temp[inx + 2], (BYTE)~0 ); } } |
- 32位位图(增强真彩位图)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
PreDecode(); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 4) { bmBuff[pos] = ExRGBA ( temp[inx], temp[inx + 1], temp[inx + 2], (BYTE)~0 ); } } |
- 3. 完整示例代码
这里给出的代码是我个人程序中的代码,自己单独使用需要按照自己的需求对其中的一些预定义及底层调用做适当的修改.
全部的图像解析及底层库代码请访问:http://code.google.com/p/win32-standard-expansion/
- CoderObject.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
////////////////////////////////////////////////////////////////// // CoderObject - 编/解码器基类 // // Author: 木头云 // Blog: darkc.at // E-Mail: memleak@darkc.at // Date: 2011-04-05 // Version: 1.0.0001.2350 // // History: // - 1.0.0001.2350(2011-04-05) = 将具体的image_t内存块申请工作统一放在ICoderObject中处理 // = ICoderObject::DeleteImage()不再断言Image参数 ////////////////////////////////////////////////////////////////// #ifndef __CoderObject_h__ #define __CoderObject_h__ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "Image/ExpImage.h" EXP_BEG ////////////////////////////////////////////////////////////////// interface ICoderObject { protected: IFileObject* m_pFile; template <DWORD SizeT> EXP_INLINE static bool CheckFile(IFileObject* pFile, const BYTE (&chkHead)[SizeT]) { if(!pFile) return false; CFileSeeker seeker(pFile); BYTE tmp_buff[SizeT] = {0}; // 判断头部 if(!pFile->Seek(0, IFileObject::begin)) return false; if (pFile->Read(tmp_buff, _countof(tmp_buff), sizeof(BYTE)) != _countof(chkHead)) return false; if (memcmp(tmp_buff, chkHead, sizeof(chkHead)) != 0) return false; return true; } EXP_INLINE static image_t GetImageBuff(LONG nWidth, LONG nHeight, BYTE*& pBuff) { if (nWidth <= 0 || nHeight <= 0) return NULL; CImage exp_image; pBuff = NULL; image_t image = exp_image.Create(nWidth, nHeight); if (image) pBuff = (BYTE*)exp_image.GetPixels(); return image; } public: ICoderObject() : m_pFile(NULL) {} ICoderObject(IFileObject* pFile) : m_pFile(NULL) { SetFile(pFile); } virtual ~ICoderObject() {} public: virtual void SetFile(IFileObject* pFile) { m_pFile = pFile; } virtual IFileObject* GetFile() { return m_pFile; } virtual bool Encode(image_t Image) = 0; virtual image_t Decode() = 0; EXP_INLINE static bool DeleteImage(image_t Image) { return Image ? CImage(Image).Delete() : true; } }; ////////////////////////////////////////////////////////////////// EXP_END #endif/*__CoderObject_h__*/ |
- BmpCoder.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
////////////////////////////////////////////////////////////////// // BmpCoder - BMP文件编/解码器 // // Author: 木头云 // Blog: darkc.at // E-Mail: memleak@darkc.at // Date: 2011-04-07 // Version: 1.0.0004.1730 // // History: // - 1.0.0001.2350(2011-04-05) ^ 优化BmpCoder的结构 // + 添加对16位位图的解析处理 // - 1.0.0002.1326(2011-04-06) = 32位位图不再解析Alpha通道 // - 1.0.0003.1544(2011-04-06) ^ 支持任何格式的16位位图解码 // - 1.0.0004.1730(2011-04-07) + 添加BmpCoder::Encode()实现 ////////////////////////////////////////////////////////////////// #ifndef __BmpCoder_h__ #define __BmpCoder_h__ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "ImgCoder/CoderObject.h" EXP_BEG ////////////////////////////////////////////////////////////////// class CBmpCoder : public ICoderObject { protected: // 拿到对齐后的字节宽度 EXP_INLINE static int PitchBytes(int nWidth, int nBitCount) { return (nWidth * nBitCount + 7) >> 3; } EXP_INLINE static int PitchWidth(int nPitByts) { return ((nPitByts + 3) >> 2) << 2; } // 4 * ( (biWidth * biBitCount + 31) / 32 ); EXP_INLINE static int PitchWidth(int nWidth, int nBitCount) { return PitchWidth(PitchBytes(nWidth, nBitCount)); } // 计算二进制中1的个数 EXP_INLINE static BYTE BitCount(DWORD nNum) { BYTE cnt = 0; while(nNum) { ++cnt; nNum &= (nNum - 1); } return cnt; } public: EXP_INLINE static bool CheckFile(IFileObject* pFile) { if(!pFile) return false; BYTE chk_head[2] = { 0x42, 0x4D }; return ICoderObject::CheckFile(pFile, chk_head); } public: CBmpCoder() : ICoderObject() {} CBmpCoder(IFileObject* pFile) : ICoderObject(pFile) {} protected: #pragma push_macro("PreDecode") #undef PreDecode #define PreDecode() int img_w = bmiInfo.bmiHeader.biWidth; int img_h = bmiInfo.bmiHeader.biHeight; int bit_c = bmiInfo.bmiHeader.biBitCount; int bit_s = bmhHead.bfOffBits; int pit_b = PitchBytes(img_w, bit_c); int pit_w = PitchWidth(pit_b); CGC gc; BYTE* temp = ExMem::Alloc<BYTE>(&gc, pit_b) //#define PreDecode EXP_INLINE static void Decode32(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 4) { bmBuff[pos] = ExRGBA ( temp[inx], temp[inx + 1], temp[inx + 2], (BYTE)~0 ); } } } EXP_INLINE static void Decode24(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 3) { bmBuff[pos] = ExRGBA ( temp[inx], temp[inx + 1], temp[inx + 2], (BYTE)~0 ); } } } EXP_INLINE static void Decode16(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 获取掩码 uint32_t mask[3] = {0}; if (bmiInfo.bmiHeader.biCompression == BI_RGB) { mask[0] = 0x7C00; mask[1] = 0x03E0; mask[2] = 0x001F; } else // BI_BITFIELDS { pFile->Seek(sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), IFileObject::begin); pFile->Read(mask, sizeof(mask), 1); } BYTE cnt_m[3] = { BitCount(mask[0]), BitCount(mask[1]), BitCount(mask[2]) }; // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++x, ++pos, inx += 2) { uint16_t pixel = *(uint16_t*)(temp + inx); bmBuff[pos] = ExRGBA ( (pixel & mask[2]) << (8 - cnt_m[2]), ((pixel & mask[1]) >> cnt_m[2]) << (8 - cnt_m[1]), ((pixel & mask[0]) >> (cnt_m[1] + cnt_m[2])) << (8 - cnt_m[0]), (BYTE)~0 ); } } } EXP_INLINE static void Decode8(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 获取调色板 RGBQUAD colors[1 << 8] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); for(int x = 0; x < img_w; ++x, ++pos) { bmBuff[pos] = ExRGBA ( colors[temp[x]].rgbBlue, colors[temp[x]].rgbGreen, colors[temp[x]].rgbRed, (BYTE)~0 ); } } } EXP_INLINE static void Decode4(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 获取调色板 RGBQUAD colors[1 << 4] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++inx) { for(int n = 1; n >= 0 && x < img_w; --n, ++x, ++pos) { BYTE c = (temp[inx] >> (4 * n)) & 0x0F; bmBuff[pos] = ExRGBA ( colors[c].rgbBlue, colors[c].rgbGreen, colors[c].rgbRed, (BYTE)~0 ); } } } } EXP_INLINE static void Decode1(IFileObject* pFile, BITMAPFILEHEADER& bmhHead, BITMAPINFO& bmiInfo, pixel_t* bmBuff) { if (!pFile || !bmBuff) return; PreDecode(); // 获取调色板 RGBQUAD colors[1 << 1] = {0}; pFile->Seek(bit_s - sizeof(colors), IFileObject::begin); pFile->Read(colors, sizeof(colors), 1); // 解析图像 for(int y = 0; y < img_h; ++y) { int pos = img_w * y; int inx = pit_w * y; pFile->Seek(bit_s + inx, IFileObject::begin); pFile->Read(temp, pit_b, 1); inx = 0; for(int x = 0; x < img_w; ++inx) { for(int n = 7; n >= 0 && x < img_w; --n, ++x, ++pos) { BYTE c = (temp[inx] >> (1 * n)) & 0x01; bmBuff[pos] = ExRGBA ( colors[c].rgbBlue, colors[c].rgbGreen, colors[c].rgbRed, (BYTE)~0 ); } } } } #pragma pop_macro("PreDecode") public: bool Encode(image_t Image) { IFileObject* file = GetFile(); if(!file) return false; CImage exp_image(Image); if (exp_image.IsNull()) return false; // 填充图像信息 BITMAPFILEHEADER file_head = {0}; file_head.bfType = 0x4D42; file_head.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + exp_image.GetSize(); file_head.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); BITMAPINFO file_info = {0}; file_info.bmiHeader.biSize = sizeof(file_info.bmiHeader); file_info.bmiHeader.biBitCount = (exp_image.GetChannel() << 3); file_info.bmiHeader.biCompression = BI_RGB; file_info.bmiHeader.biPlanes = 1; file_info.bmiHeader.biWidth = exp_image.GetWidth(); file_info.bmiHeader.biHeight = exp_image.GetHeight(); // 写入文件 file->Seek(0, IFileObject::begin); file->Write(&file_head, sizeof(file_head), 1); file->Write(&file_info, sizeof(file_info), 1); file->Write(exp_image.GetPixels(), exp_image.GetSize(), 1); file->Seek(0, IFileObject::begin); return true; } image_t Decode() { IFileObject* file = GetFile(); if(!CheckFile(file)) return NULL; CFileSeeker seeker(file); // 获取图像信息 BITMAPFILEHEADER file_head = {0}; file->Read(&file_head, sizeof(file_head), 1); BITMAPINFO file_info = {0}; file->Read(&file_info, sizeof(file_info), 1); if (file_info.bmiHeader.biCompression != BI_RGB && file_info.bmiHeader.biCompression != BI_BITFIELDS) return NULL; // 根据图像信息申请一个图像缓冲区 pixel_t* bmbf = NULL; image_t image = GetImageBuff(file_info.bmiHeader.biWidth, file_info.bmiHeader.biHeight, (BYTE*&)bmbf); if(!image) return NULL; // 解析图像信息 switch (file_info.bmiHeader.biBitCount) { case 32: Decode32(file, file_head, file_info, bmbf); break; case 24: Decode24(file, file_head, file_info, bmbf); break; case 16: Decode16(file, file_head, file_info, bmbf); break; case 8: Decode8(file, file_head, file_info, bmbf); break; case 4: Decode4(file, file_head, file_info, bmbf); break; case 1: Decode1(file, file_head, file_info, bmbf); break; } // 返回image_t return image; } }; ////////////////////////////////////////////////////////////////// EXP_END #endif/*__BmpCoder_h__*/ |
在参考了相关资料以后,发现上面32位位图解析有问题.
32位位图内存像素的排列是 XRGB(X为无效位).
因此正确的解析,在14-16行应该是:
// …
temp[inx + 1],
temp[inx + 2],
temp[inx + 3],
// …