如果你的意图是做一个显示BMP文件的程序,那么请参阅BMP文件详细格式分析,如果你仅仅是想知道怎么从一个未压缩的24位色BMP文件中把数据点阵信息读出,用来做其它的用途(当然也包括显示图像),那么这篇文章会对你有帮助。
虽然有很复杂的定义,但是我们平时使用的BMP文件都多是8位色或24位色的,而这两种格式中,以24位色最为普遍。可以通过查看文件偏移量地址001C,得到图形的色数。
查看地址0000起的两个字节,如果ACSII码显示为“BM”,可判断这是一个BMP文件。确切的说这是一个Windows下的BMP文件。
查看地址000A起的一个双字(DWord),可以得到文件信息头和图像信息头的总长度,这很重要,通过读取它,可以用于文件指针的Seek,找到数据区的起点。
至于图像文件的宽和高,可以通过0012和0016起的两个双字来获得。
紧跟在文件信息头和数据信息头之后的就是数据区,24位色BMP图没有调色板。
数据区里的数据是线性的,行主序,依次是 点一的B值,点一的G值,点一的R值,点二的B值,点二的G值,点二的R值,等等,需要注意的是,Windows中普遍采用了行倒向扫描的约定,即,BMP文件中原点在左下角,向上,向右,增加。这一点,需要在将数据区数据向结构体数组读取的时候,做一个小技巧,如果不做倒向的取值,你将会显示出一个颠倒的图像。(当然,这是指用Windows GDI画图,如果你使用OpenGL,你将会得到一个正向的图)
还有一点也需要注意:Windows要求每一行的数据的长度必须是4Bytes的整数倍,如果不是,要以值为0的字节补充,如果读取的时候不处理,会得到一个倾斜的图像。举例:一张479*360的24位色BMP图片,宽度是479个像素,每一行的数据长度是479*3(个字节用于RGB三色) =1437,不是4的倍数,所以,在这行数据的后面,要补上3个字节,值为0,凑成1440,达到4的倍数,然后,才是下一行的数据。在做程序的时候,需要把这些冗余点的值舍去。
看到这里就可以了,我有一个新的代码实现,比下面的要好,强烈推荐你使用
void CTextureyView::OnFileOpen()
{
CFileDialog dlg(true);
if(IDOK==dlg.DoModal())
{
FileName=dlg.GetFileName();
Invalidate(true);
}//IDOK
}
void CTextureyView::OnDraw(CDC* pDC)
{
CTextureyDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (FileName!="")
{
typedef struct
{
BYTE b;
BYTE g;
BYTE r;
}structRGB;
DWORD fw,fh;//图像的宽和高
DWORD hl;//文件头和信息头的总长
CFile* pFile;
try
{
pFile=new CFile(FileName,CFile::modeRead);
pFile->Seek(10,CFile::begin);
pFile->Read(&hl,4);
pFile->Seek(0x12,CFile::begin);
pFile->Read(&fw,4);
pFile->Seek(0x16,CFile::begin);
pFile->Read(&fh,4);
int att;//用于处理行扫描时的4补位
if((fw*3)%4!=0)
att=4-(fw*3)%4;
else
att=0;
structRGB** srgb=new structRGB*[fh];
for(int s=0;s<fh;s++)
srgb[s]=new structRGB[fw];
BYTE* pBuffer= new BYTE[3*fw*fh];
pFile->Seek(hl,CFile::begin);
pFile->Read(pBuffer,3*fw*fh);
for(int i=0;i<fh;i++)
{
for(int j=0;j<fw;j++)
{
srgb[fh-1-i][j].b=pBuffer[i*(fw*3+att)+j*3];
srgb[fh-1-i][j].g=pBuffer[i*(fw*3+att)+j*3+1];
srgb[fh-1-i][j].r=pBuffer[i*(fw*3+att)+j*3+2];
}
}
for(int n=0;n<fh;n++)
for(int m=0;m<fw;m++)
{
pDC->SetPixel(m,n,RGB(srgb[n][m].r,srgb[n][m].g,srgb[n][m].b));
}
}
catch(CFileException* pe)
{
pe->ReportError();
if(pFile)
{
delete pFile;
}
}
pFile->Close();
}//if FileName!=NULL
}