Chinaunix首页 | 论坛 | 博客
  • 博客访问: 547752
  • 博文数量: 119
  • 博客积分: 3391
  • 博客等级: 中校
  • 技术积分: 981
  • 用 户 组: 普通用户
  • 注册时间: 2009-03-12 11:49
文章分类

全部博文(119)

文章存档

2014年(3)

2013年(1)

2011年(18)

2010年(27)

2009年(70)

我的朋友

分类: 服务器与存储

2009-03-18 18:55:56

有无数种不同的文件格式,但通常只使用很少的几个进行游戏编程:.PCX(PC Paint)、.TGA(Targa)、.BMP(Windows内置格式)。他们各有优缺点,但在Windows下工作,所以最好采用Windows本身的格式——.BMP。

其它格式工作原理相通,你可以自己写一个阅读器,也可以用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;indexbitmapinfoheader.biHeight;index++)
{
memcpy(bitmap->buffer[bspl*index]),Cbuffer[bspl*(bitmap->bitmapinfoheader.biHeight-index-1)],bspl);
}
return(1);
//结束了
}

这个函数够长的,记得在退出程序时要释放bitmap->buffer,如果不释放也无关紧要,还记得在函数中有专门的代码清空它么?

##提示
函数最后的“转换数据顺序”部分并没有经过测试,拉莫泽在此处有专门的Flip_Bitmap()函数处理数据。
##
阅读(1105) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~