如何将位图对象保存为BMP文件收藏新一篇: PE文件格式与API HOOK |GDI中位图对象是很常见的GDI对象,但是无论是SDK,还是MFC都没有提供现在的函数或是方法来将一个位图对象保存为一个BMP文件,这里介绍一下保存方法。 位图文件格式: DIB文件有四个主要部分: 文件表头(BITMAPFILEHEADER) 信息表头 (BITMAPINFOHEADER) 调色板(不一定有) 位图图素位 而一个位图对象和上述唯一不同在于它没有文件表头。 相关数据结构: (1)文件表头 typedef struct tagBITMAPFILEHEADER { WORD bfType; //BMP文件类型,总是字符BM,十六进制为0x4d42 DWORD bfSize; //BMP文件大小,包含这个结构在内。 WORD bfReserved1; WORD bfReserved2; //以上均保留为0 DWORD bfOffBits; //是一个偏移量,指出了文件中图素位开始位置的字节偏移量 } BITMAPFILEHEADER, *PBITMAPFILEHEADER; (2)信息表头 typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //结构的大小 LONG biWidth; //位图的宽度 LONG biHeight; //位图的高度 WORD biPlanes; //必须是1 WORD biBitCount; //指出每一个像素要用的bit位。 DWORD biCompression; //指出是否是压缩的,以及压缩方式 DWORD biSizeImage; //指出图像的尺寸 LONG biXPelsPerMeter; //水平基线 LONG biYPelsPerMeter; //坚直基线 DWORD biClrUsed; //被用的颜色数 DWORD biClrImportant; //重要的颜色数 } BITMAPINFOHEADER, *PBITMAPINFOHEADER; (3)调色板结构: typedef struct tagRGBQUAD // rgb { BYTE rgbBlue ; // blue level BYTE rgbGreen ; // green level BYTE rgbRed ; // red level BYTE rgbReserved ; // = 0 }RGBQUAD ; 注意这个结构应该是一个数组,在256色及以下的BMP文件中存在,数组的长度关键看颜色数。 BITMAP定义了一个位图的类型、长度、宽度、颜色格式等,这个结构一般用GetObject来获得。定义如下:typedef struct tagBITMAP { LONG bmType; //类型,不过总是为0 LONG bmWidth; //宽度,总是大于0 LONG bmHeight; //高度,总是大于0 LONG bmWidthBytes; //MSDN上解释说是指定每一个扫描行的字节数。 WORD bmPlanes; //指定调色板数目 WORD bmBitsPixel; //指示一个像素所要求的byte位 LPVOID bmBits; //指定一个数组指针,这个数组大约应该是保存位图数据的。 } BITMAP, *PBITMAP 一个位图对象也就是存在内存中的位图,它与存在硬盘上的BMP文件相比,唯一的区别就是它没有BITMAPFILEHEADER这个文件信息头,其余部分是完全相同的,所以我们要做的就是先构造一个文件信息头,写入文件中,然后将内存中的位图写入文件。 源代码如下:(只写主要部分) WORD wbitsCount;//位图中每个像素所占字节数。 DWORD dwpalettelsize=0;//调色板大小 DWORD dwbmdibitsize,dwdibsize,dwwritten; BITMAP bitmap;//定义了位图的各种的信息。 BITMAPFILEHEADER bmfhdr;//定义了大小、类型等BMP文件的信息。 BITMAPINFOHEADER bi; LPBITMAPINFOHEADER lpbi; HANDLE fh,fdib; GetObject(hBitmap,sizeof(BITMAP),(void *)&bitmap);//得到BITMAP结构。 //以下代码是用BITMAP的信息填充BITMAPINFOHEADER结构 wbitsCount=bitmap.bmBitsPixel; bi.biSize=sizeof(BITMAPINFOHEADER); bi.biWidth=bitmap.bmWidth; bi.biHeight=bitmap.bmHeight; bi.biPlanes=1; bi.biBitCount= bitmap.bmBitsPixel ; bi.biClrImportant=0; bi.biClrUsed=0; bi.biCompression=BI_RGB; bi.biSizeImage=0; bi.biYPelsPerMeter=0; bi.biXPelsPerMeter=0; //以下代码是获取调色板的长度,调色板现在的用处很少,因为256色的位图已经不多了。 if(wbitsCount<=8) dwpalettelsize=(1<<wbitsCount)*sizeof(RGBQUAD); //计算位图的大小,并分配相应的内存空间,注意的是没有分配BITMAPFILEHEADER。 dwbmdibitsize=((bitmap.bmWidth*wbitsCount+31)/8)*bitmap.bmHeight; fdib=GlobalAlloc(GHND,dwbmdibitsize+dwpalettelsize+sizeof(BITMAPINFOHEADER)); lpbi=(LPBITMAPINFOHEADER)::GlobalLock(fdib); *lpbi=bi;//将bi中的数据写入分配的内存中。 hdc=::GetDC(NULL); GetDIBits(hdc,hBitmap,0,(UINT)bitmap.bmHeight,(LPSTR)lpbi+sizeof(BITMAPINFOHEADER)+dwpalettelsize,(BITMAPINFO *)lpbi,DIB_RGB_COLORS); /*GetDIBits是最重要的函数,真正获得位图数据的工作就由它完成,它第一个参数为HDC,第二个参数为位图句柄,第三个参数为扫描行的开始行,一般为0,第四个为结束行,一般就是高度,第四个参数最重要,它表示接收数据的起始地址,这个地址一般是在调色板之后。第五个参数指的是接收BITMAPINFO结构的地址,这个结构上面没有写,它其实就是BITMAPINFO结构加上调色板信息。最后一个参数是格式。一般是DIB_RGB_COLORS*/ //创建文件以及文件信息头 fh=CreateFile(FileName,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN,NULL); if(fh==INVALID_HANDLE_VALUE) return FALSE; bmfhdr.bfType=0x4d42;//BMP类型,一定要这样写 dwdibsize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwbmdibitsize+dwpalettelsize;//文件总长,由几个部分组成 bmfhdr.bfSize=dwdibsize; bmfhdr.bfReserved1=0; bmfhdr.bfReserved2=0; bmfhdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+dwpalettelsize;//位图数据相对于文件头的偏移量 //将文件信息头写入文件 WriteFile(fh,(LPSTR)&bmfhdr,sizeof(BITMAPFILEHEADER),&dwwritten,NULL); //将数据写入文件,包含BITMAPINFO结构、调色板、数据 WriteFile(fh,(LPSTR)lpbi,dwdibsize,&dwwritten,NULL); //关闭相关句柄 ::GlobalUnlock(fdib); ::GlobalFree(fdib); ::CloseHandle(fh); return TRUE; |
|