分类: 服务器与存储
2009-03-18 18:55:56
其它格式工作原理相通,你可以自己写一个阅读器,也可以用Win32 API函数,或者两者混合使用。用于弄清楚Win32函数同自己编写一个地难度差不多,你可能更愿意写一个.BMP装载文件。.BMP文件由三部分组成:
1。位图文件头:它存放着图像的总体信息,存放在Win32数据结构BITMAPFILEHEADER中:
typedef struct tagBITMAPFILEHEADER
{
WORD bfType;//指定文件类型为位图
DWORD bfSize;//位图文件头大小,14字节
WORD bfReserved1;//保留字,不用多管
WORD bfReserved2;//保留字,不用多管
DWORD bfOffBits;//文件头到数据区的字节偏移量
}BITMAPFILEHEADER;
2。位图信息段:包括两部分数据,即BITMAPINFOHEADER部分和调色板信息部分(如果有的话)。
3。 位图数据区:它是一个字节流,描述了1、4、8、16、24位图象素(压缩或未压缩格式)。数据是逐行排列的(这是与矢量图的最大区别),但是有时数据也 会倒过来填充,也就是说位图数据区的第一行其实是图像的最后一行。在BITMAPINFOHEADER结构中,你可以通过其成员biHeight的正负号 来知道这点——正值代表颠倒,负值表示正常。
下面详细介绍位图信息段的核心结构BITMAPINFOHEADER:
typedef struct tapBITMAPINFOHEADER
{
DWORD biSize;//指定bitmapinfoheader结构本身的字节数,大小40字节
LONG biWidth;//指定位图以象素计的宽度
LONG biHeight;//指定位图以象素计的高度
WORD biPlanes;//位平面,总是1
WORD biBitCount;//指定位图每象素所占的位数,值可以是8、16、24
DWORD biCompression;//指定位图是否经过压缩
DWORD biSizeImage;//指定位图数据区总计字节数
LONG biXPelsPerMeter;//目标设备x轴分辨率,每米象素数
LONG biYPelsPerMeter;//目标设备y轴分辨率,每米象素数
DWORD biClrused;//0,表示每象素字节数按2的biBitCount次幂计算
DWORD biClrImportant;//0,表示所有颜色都是重要颜色
}BITMAPINFOHEADER;
##
总体上来说,BITMAPINFOHEADER同调色板项的作用相似,主要是对位图的各种属性定位,不同的是前者工作在位图每象素高于8字节的高彩模式,后者只有象素模式为8位也就是256色模式的时候才出现
##
为 了自行读取一个.BMP文件,首先需要打开它(你可以用你喜欢的任何I/O技术),然后读BITMAPFILEHEADER。接着读 BITMAPINFOHEADER和调色板项(不管有没有)。借此你可以得出图像的大小、色彩深度、接着你读出图像数据和调色板。当然,这里有许多细节, 如分配内存来读取数据、移动文件指针等。同时调色板项是RGBQUAD,是正常的PALETTEENTRY结构的倒序,你可以看一下它的结构:
typedef struct tagRGBQUAD
{
BYTE pebBlue;
BYTE pebGreen;
BYTE pebRed;
BYTE rgbReserbed;
}RGBQUAD;
下面来看WINDOWS API自带的一个装载位图的函数:
HANDLE LoadImage(HINSTANCE hinst,
//指向.exe的全局句炳,从头到尾只是一个标识
LPCTSTR lpszName,//磁盘上的.BMP文件名,
UINT uType,//如果你要装入位图就把它设为
//IMAGE_BITMAP
int cxEdsired,//位图应有的高度,设为零
intcyDesired,//位图应有的高度
UINT fuLoad)//一个控制标志,你需要将它设为
//LR_LOADFROMFILE|LR_CREATEDIBSECTION,它
//通知LoadImage()用lpszName作文件名从磁盘读取
LoadImage()函数的缺点在于它太通用、保守,获得一点文件细节几乎是不可能的。安德鲁·拉莫泽定义了一个BITMAP_FILE结构,通过一个读图函数将任何类型地位图数据读入其中。下面就是装载位图数据的结构:
typedef struct BITMAP_FILE_TAG
{
BITMAPFILEHEADER bitmapfileheader;
BITMAPINFOHEADER bitminfoheader;
PALETTEENTRY palette[256];
UCHAR *buffer;
}BITMAP_FILE,FAR *BITMAP_FILE_PTR;
拉莫泽将BITMAPFILEHEADER和BITMAPINFOHEADER放在一个结构中,用起来非常方便。下面是Load_Bitmap_File()函数:
int Load_Bitmap_File(BITMAP_FILE_PTR bitmap,char *filename)
//返回类型无符号整型,带两个参数,bitmap是即将要将位图数据读入的
//结构,filename是将要从硬盘读入的文件名,缺了谁都不行;在类中,
//当你将这个函数放到public声明中的时候,可以不待参数,而将这两个
//变量分别当作private声明中的数据成员。
{
int file_handle,index;//保存打开位图函数OpenFile()的返回值
//index用在后面for循环的计数
UCHAR *temp_buffer=NULL;//24位转为16位模式用到的中间指针
OFSTRUCT file_data;//打开位图时保存数据的OFSTRCUT结构
//首先打开位图:
if((file_handle=OpenFile(filename,&file_data,OF_READ))==-1)
return(0);//如果读到文件结尾,返回到主函数
//装载相应信息至bitmap的位图文件头
_lread(file_handle,&bitmap->bimtapfileheader,sizeof(BITMAPFILEHEADER);
//察看位图文件头bfType结构,是否是位图标识:
if(bitmapfileheader.bfType!=BITMAP_ID)
{//如果不是,关闭文件句柄;
_lcolse(file_handle);
return(0);
}
//如果是位图,接下来获取进一步信息
//装载相应信息至bitmap的位图信息段
_lread(file_handle,&bitmap->bitmapinfoheader,sizeof(BITMAPINFOHEADER);
//如果位图象素格式是8,那么首先需要读取调色板:
if(bitmap->bitmapinfoheader.biBitCount==8)
{
//装载相应信息至bitmap的调色板项
_lread(file_handle,&bitmap->palette,sizeof(PALETTEENTRY)*256);
//调换peRed和peBlue项
for(index=0;index<256;index++)
{int temp_buffer=bitmap->palette[index].peRed;
bitmap->palette[index].peRed=bitmap->palette[index].peBlue;
bitmap->palette[index].peBlue=temp_buffer;
}
}
//从文件末尾产生biSizeImage大小的字节偏移
//使位图数据区被写入bitmap->buffer时不会产生误差
_lseek(file_handle,-(int)(bitmap->bitmapinfoheader.biSizeImage,SEEK_END);
//开始读写位图数据的操作,首先分配一个UCHAR型缓冲区
if(bitmap->bitmapinfoheader.biBitCount==8||bitmap->bitmapinfoheader.biBitCount==16||bitmp->bitmapinfoheader.biBitCount==24)
{
if(bitmap->buffer)
free(bitmap->buffer);
if(!(bitmap->buffer=(UCHAR *)malloc(bitmap-bitmapinfoheader.biSizeImage)))
{_lclose(file_handle);
return(0);
}
_lread(file_handle,bitmap->buffer,bitmap->bitmapinfoheader.biHeight);
}
else
{return(0);}
//完成数据读写后关闭文件句柄
_lclose(file_handle);
//转换数据顺序
UCHAR *Cbuffer=(UCHAR *)malloc(bitmap->bitmapinfoheader.biSizeImage);
memcpy(Cbuffer,bitmap->buffer,bitmap->bitmapinfoheader.biSizeImage);
//位图每行的字节数
int bspl=bitmap->biWidth*(bitmap->bitmapinfoheader.biBitCount/8;
for(index=0;index
{
memcpy(bitmap->buffer[bspl*index]),Cbuffer[bspl*(bitmap->bitmapinfoheader.biHeight-index-1)],bspl);
}
return(1);
//结束了
}
这个函数够长的,记得在退出程序时要释放bitmap->buffer,如果不释放也无关紧要,还记得在函数中有专门的代码清空它么?
##提示