在VC中,位图显示一般有现成的方式,如使用picture控件、GetDC()->StretchBlt、::BitBlt等,但这些方式都是高层的封装,让你不清楚一副位图是如何解析并显示到DC上的。实际应用中,比如图像处理,视频显示等,需要操作到位图中的像素,这需要弄明白位图文件如何组成,网上有太多的位图文件格式说明,下面借助实例和SetPixel函数完成解析与显示。
读入一幅位图,结合位图文档说明,按着F5、F10把程序走一遍,你就会把位图弄的明明白白。
void CTestDlg::OnButton1()
{
//读入位图文件名
char *filename = "720bmp16.bmp";
CDC *pDC = GetDC();
//
// 一次性将位图文件读入到内容中,待处理
//
CFile f;
f.Open(filename, CFile::modeRead);
char* buff = (char*)malloc(f.GetLength());
f.SeekToBegin();
f.Read(buff, f.GetLength());
//
BITMAPFILEHEADER *fileheader = NULL; //数据结构大小为14
BITMAPINFO *info = NULL; //数据结构大小为40
fileheader = (BITMAPFILEHEADER*)buff;
if(fileheader->bfType!=0x4D42)
{
AfxMessageBox("不是BMP文件");
f.Close();
return ;
}
//位图信息区域,在偏移14处
info = (BITMAPINFO*)(buff+(sizeof(BITMAPFILEHEADER)));
//位图的宽和高
int width = info->bmiHeader.biWidth;
int height= info->bmiHeader.biHeight;
//指向位图像素区域
char* buffer = buff+fileheader->bfOffBits;
//
// 显示位图的一些基本信息
//
CString str;
str.Format("位图大小= %d\n"
"位图数据起始偏移 = %d\n"
"BITMAPINFOHEADER.biSize = %d\n"
"宽=%d, 高=%d\n"
"颜色位数=%d\n"
"压缩=%d\n"
"biSizeImage=%d\n"
"biClrUsed=%d\n"
"biClrImportant=%d",
fileheader->bfSize, //位图大小
fileheader->bfOffBits, //位图数据起始偏移
info->bmiHeader.biSize,
info->bmiHeader.biWidth,
info->bmiHeader.biHeight,
info->bmiHeader.biBitCount,
info->bmiHeader.biCompression,
info->bmiHeader.biSizeImage,
info->bmiHeader.biClrUsed,
info->bmiHeader.biClrImportant
);
AfxMessageBox(str);
int i,j;
//
// 单色图的解析
//
if(info->bmiHeader.biBitCount==1)
{
int n=0;
int color[500][500];
if(height>0)
{
//height>0 表示图片颠倒
for(i=0; i<height; i++)
for(j=0; j<width; j=j+8)
{
int k=7;
while(k>=0)
{
color[i][k+j]=buffer[n]%2;
buffer[n]=buffer[n]/2;
k--;
}
n++;
}
for(i=0; i<height; i++)
for(j=0; j<width; j++)
{
if(color[i][j] == 0)
{
pDC->SetPixel(j,height-i,RGB(0,0,0));
}
if(color[i][j] == 1)
{
pDC->SetPixel(j,height-i,RGB(255,255,255));
}
}
}
else
{
for(i=0; i<0-height; i++)
for(j=0; j<width; j=j+8)
{
int k=7;
while(k>=0)
{
color[i][k+j]=buffer[n]%2;
buffer[n]=buffer[n]/2;
k--;
}
n++;
}
for(i=0; i<0-height; i++)
for(j=0; j<width; j++)
{
if(color[i][j] == 0)
{
pDC->SetPixel(j,i,RGB(0,0,0));
}
if(color[i][j] == 1)
{
pDC->SetPixel(j,i,RGB(255,255,255));
}
}
}
}
//
// 16色图的解析
//
else if(info->bmiHeader.biBitCount==4)
{
int pitch;
if(width%8==0)
pitch=width;
else
pitch=width+8-width%8;
//调色板数据
RGBQUAD quad[16];
memcpy(quad, buff+fileheader->bfOffBits-sizeof(quad), sizeof(quad));
//指向真正的像素数据
buffer = buff+fileheader->bfOffBits;
if(height>0)
{
//height>0 表示图片颠倒
for(i=0; i<height; i++)
for(j=0; j<width; j++)
{
int index;
if(j%2==0)
index = buffer[(i*pitch+j)/2]/16;
if(j%2==1)
index = buffer[(i*pitch+j)/2]%16;
unsigned char r=quad[index].rgbRed;
unsigned char g=quad[index].rgbGreen;
unsigned char b=quad[index].rgbBlue;
pDC->SetPixel(j,height-i,RGB(r,g,b));
}
}
else
{
for(i=0; i<0-height; i++)
for(j=0; j<width; j++)
{
int index;
if(j%2==0)
index = buffer[(i*pitch+j)/2]/16;
if(j%2==1)
index = buffer[(i*pitch+j)/2]%16;
unsigned char r=quad[index].rgbRed;
unsigned char g=quad[index].rgbGreen;
unsigned char b=quad[index].rgbBlue;
pDC->SetPixel(j,i,RGB(r,g,b));
}
}
}
//
// 256色(8位)图的解析
//
else if(info->bmiHeader.biBitCount==8)
{
int pitch;
if(width%4==0)
{
pitch=width;
}
else
{
pitch=width+4-width%4;
}
//8位位图,有调试板数据
RGBQUAD quad[256] = {0};
memcpy(quad, buff+fileheader->bfOffBits-sizeof(quad), sizeof(quad));
// 指向像素数据的起始偏移(其实并不是像素,8位位图的“像素”值代入调色板的下标,
// 得到的值才是真正的像素)
buffer = buff+fileheader->bfOffBits;
//显示
if(height>0)
{
//height>0 表示图片颠倒
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
int index=buffer[i*pitch+j];
UCHAR r=quad[index].rgbRed;
UCHAR g=quad[index].rgbGreen;
UCHAR b=quad[index].rgbBlue;
pDC->SetPixel(j, height-i, RGB(r,g,b));
}
}
}
else
{
for(int i=0;i<0-height;i++)
{
for(int j=0;j<width;j++)
{
int index=buffer[i*pitch+j];
UCHAR r=quad[index].rgbRed;
UCHAR g=quad[index].rgbGreen;
UCHAR b=quad[index].rgbBlue;
pDC->SetPixel(j,i,RGB(r,g,b));
}
}
}
}
//
// 65536色(16位)图解析
//
else if(info->bmiHeader.biBitCount==16)
{
int pitch=width+width%2;
if(height>0)
{
//height>0 表示图片颠倒
if(info->bmiHeader.biCompression==BI_RGB)
{
//该模式只有555
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
// 555 格式
unsigned char b=buffer[(i*pitch+j)*2]&0x1F;
unsigned char g=(((buffer[(i*pitch+j)*2+1]<<6)&0xFF)>>3)+(buffer[(i*pitch+j)*2]>>5);
unsigned char r=(buffer[(i*pitch+j)*2+1]<<1)>>3;
pDC->SetPixel(j,height-i,RGB((r*0xFF)/0x1F,(g*0xFF)/0x1F,(b*0xFF)/0x1F));
}
}
}
if(info->bmiHeader.biCompression==BI_BITFIELDS)
{
//该模式在bitmapinfoheader之后存在RGB掩码 每个掩码1 DWORD
//fseek(fp,fileheader.bfOffBits-sizeof(DWORD )*3,0);
DWORD rMask;
//fread(&rMask,sizeof(DWORD ),1,fp);
memcpy(&rMask, buff+fileheader->bfOffBits-sizeof(DWORD )*3, sizeof(DWORD ));
if(rMask==0x7C00)
{
// 5 5 5 格式
//MessageBeep(0);
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
unsigned char b=buffer[(i*pitch+j)*2]&0x1F;
unsigned char g=(((buffer[(i*pitch+j)*2+1]<<6)&0xFF)>>3)+(buffer[(i*pitch+j)*2]>>5);
unsigned char r=(buffer[(i*pitch+j)*2+1]<<1)>>3;
pDC->SetPixel(j,height-i,RGB((r*0xFF)/0x1F,(g*0xFF)/0x1F,(b*0xFF)/0x1F));
}
}
}
if(rMask==0xF800)
{
//5 6 5 格式
for(int i=0;i<height;i++)
{
for(int j=0;j<width;j++)
{
UCHAR b=buffer[(i*pitch+j)*2]&0x1F;
UCHAR g=(((buffer[(i*pitch+j)*2+1]<<5)&0xFF)>>2)+(buffer[(i*pitch+j)*2]>>5);
UCHAR r=buffer[(i*pitch+j)*2+1]>>3;
pDC->SetPixel(j,height-i,RGB(r*0xFF/0x1F,g*0xFF/0x3F,b*0xFF/0x1F));
}
}
}
}
}
else
{
if(info->bmiHeader.biCompression==BI_RGB)
{
//该模式只有555
for(int i=0;i<0-height;i++){
for(int j=0;j<width;j++){
//5 5 5 格式
UCHAR b=buffer[(i*pitch+j)*2]&0x1F;
UCHAR g=(((buffer[(i*pitch+j)*2+1]<<6)&0xFF)>>3)+(buffer[(i*pitch+j)*2]>>5);
UCHAR r=(buffer[(i*pitch+j)*2+1]<<1)>>3;
pDC->SetPixel(j,i,RGB((r*0xFF)/0x1F,(g*0xFF)/0x1F,(b*0xFF)/0x1F));
}
}
}
if(info->bmiHeader.biCompression==BI_BITFIELDS)
{
//该模式在bitmapinfoheader之后存在RGB掩码 每个掩码1 DWORD
//fseek(fp,fileheader.bfOffBits-sizeof(DWORD )*3,0);
DWORD rMask;
//fread(&rMask,sizeof(DWORD ),1,fp);
memcpy(&rMask, buff+fileheader->bfOffBits-sizeof(DWORD )*3, sizeof(DWORD ));
if(rMask==0x7C00)
{
// 5 5 5 格式
MessageBeep(0);
for(int i=0;i<0-height;i++)
{
for(int j=0;j<width;j++)
{
UCHAR b=buffer[(i*pitch+j)*2]&0x1F;
UCHAR g=(((buffer[(i*pitch+j)*2+1]<<6)&0xFF)>>3)+(buffer[(i*pitch+j)*2]>>5);
UCHAR r=(buffer[(i*pitch+j)*2+1]<<1)>>3;
pDC->SetPixel(j,i,RGB((r*0xFF)/0x1F,(g*0xFF)/0x1F,(b*0xFF)/0x1F));
}
}
}
if(rMask==0xF800)
{
//565 格式
for(int i=0;i<0-height;i++)
{
for(int j=0;j<width;j++)
{
UCHAR b=buffer[(i*pitch+j)*2]&0x1F;
UCHAR g=(((buffer[(i*pitch+j)*2+1]<<5)&0xFF)>>2)+(buffer[(i*pitch+j)*2]>>5);
UCHAR r=buffer[(i*pitch+j)*2+1]>>3;
pDC->SetPixel(j,i,RGB(r*0xFF/0x1F,g*0xFF/0x3F,b*0xFF/0x1F));
}
}
}
}
}
}
//
// 24位图解析
//
else if(info->bmiHeader.biBitCount==24)
{
int pitch=width%4;
// bgr
int i,j;
if(height>0)
{
//height>0 表示图片颠倒
for(i=0;i<height;i++)
{
int realPitch=i*pitch;
for(j=0;j<width;j++)
{
unsigned char b=buffer[(i*width+j)*3+realPitch];
unsigned char g=buffer[(i*width+j)*3+1+realPitch];
unsigned char r=buffer[(i*width+j)*3+2+realPitch];
pDC->SetPixel(j,height-i,RGB(r,g,b));
}
}
}
else
{
for(i=0;i<0-height;i++)
{
int realPitch=i*pitch;
for(j=0;j<width;j++)
{
unsigned char r=buffer[(i*width+j)*3+realPitch];
unsigned char g=buffer[(i*width+j)*3+1+realPitch];
unsigned char b=buffer[(i*width+j)*3+2+realPitch];
pDC->SetPixel(j,i,RGB(r,g,b));
}
}
}
}
//
// 32位图进行解析
//
else if(info->bmiHeader.biBitCount==32)
{
// bgra
if(height>0)
{
//height>0 表示图片颠倒
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
unsigned char b=buffer[(i*width+j)*4];
unsigned char g=buffer[(i*width+j)*4+1];
unsigned char r=buffer[(i*width+j)*4+2];
pDC->SetPixel(j,height-i, RGB(r,g,b));
}
}
}
else
{
for(i=0;i<0-height;i++)
{
for(j=0;j<width;j++)
{
unsigned char b=buffer[(i*width+j)*4];
unsigned char g=buffer[(i*width+j)*4+1];
unsigned char r=buffer[(i*width+j)*4+2];
pDC->SetPixel(j, i, RGB(r,g,b));
}
}
}
}
f.Close();
buff = NULL;
free(buff);
ReleaseDC(pDC);
}