在VC中,位图显示一般有现成的方式,如使用picture控件、GetDC()->StretchBlt、::BitBlt等,但这些方式都是高层的封装,让你不清楚一副位图是如何解析并显示到DC上的。实际应用中,比如图像处理,视频显示等,需要操作到位图中的像素,这需要弄明白位图文件如何组成,网上有太多的位图文件格式说明,下面借助实例和SetPixel函数完成解析与显示。
读入一幅位图,结合位图文档说明,按着F5、F10把程序走一遍,你就会把位图弄的明明白白。
源码打包放在:http://download.csdn.net/detail/dijkstar/7014189
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); }