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;