您的位置:首页 > 其它

实现BMP图片的保存和自绘图形的BMP保存

2014-03-30 11:09 405 查看
之前做项目的时候早就在网上看到过一些关于BMP图片的操作,可是当时用完后自己又忘记了,所以这次来稍稍整理下,今后要是遇到更好的方法再来补充:

一:VC++实现将客户区存为BMP

在view类中添加以下三个函数:

BOOL CTestestView::WriteWindowToDIB(LPTSTR szFile, CWnd *pWnd)

{

CBitmap bitmap;

CWindowDC dc(pWnd);

CDC memDC;

CRect rect;

memDC.CreateCompatibleDC(&dc);

pWnd->GetWindowRect(rect);

bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());

CBitmap* pOldBitmap=memDC.SelectObject(&bitmap);

memDC.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);

CPalette pal;

if(dc.GetDeviceCaps(RASTERCAPS)&RC_PALETTE)

{

UINT nSize=sizeof(LOGPALETTE)+(sizeof(PALETTEENTRY)*256);

LOGPALETTE* pLP=(LOGPALETTE*)new BYTE[nSize];

pLP->palVersion=0x300;

pLP->palNumEntries=GetSystemPaletteEntries(dc,0,255,pLP->palPalEntry);

pal.CreatePalette(pLP);

delete[] pLP;

}

memDC.SelectObject(pOldBitmap);

HANDLE hDIB=DDBToDIB(bitmap,BI_RGB,&pal);

if(hDIB==NULL)

return FALSE;

WriteDIB(szFile,hDIB);

GlobalFree(hDIB);

return TRUE;

}

HANDLE CTestestView::DDBToDIB(CBitmap &bitmap, DWORD dwCompression, CPalette *pPal)

{

BITMAP bm;

BITMAPINFOHEADER bi;

LPBITMAPINFOHEADER lpbi;

DWORD dwLen;

HANDLE hDIB;

HANDLE handle;

HDC hDC;

HPALETTE hPal;

ASSERT(bitmap.GetSafeHandle());

if(dwCompression==BI_BITFIELDS)

return NULL;

hPal=(HPALETTE)pPal->GetSafeHandle();

if(hPal==NULL)

hPal=(HPALETTE)GetStockObject(DEFAULT_PALETTE);

bitmap.GetObject(sizeof(bm),(LPSTR)&bm);

bi.biSize=sizeof(BITMAPINFOHEADER);

bi.biWidth=bm.bmWidth;

bi.biHeight=bm.bmHeight;

bi.biPlanes=1;

bi.biBitCount=bm.bmPlanes*bm.bmBitsPixel;

bi.biCompression=dwCompression;

bi.biSizeImage=0;

bi.biXPelsPerMeter=0;

bi.biYPelsPerMeter=0;

bi.biClrImportant=0;

bi.biClrUsed=0;

int nColors=(1<<bi.biBitCount);

if(nColors>256)

nColors=0;

dwLen=bi.biSize+nColors*sizeof(RGBQUAD);

hDC=::GetDC(NULL);

hPal=SelectPalette(hDC,hPal,FALSE);

RealizePalette(hDC);

hDIB=GlobalAlloc(GMEM_FIXED,dwLen);

if(!hDIB)

{

SelectPalette(hDC,hPal,FALSE);

::ReleaseDC(NULL,hDC);

return NULL;

}

lpbi=(LPBITMAPINFOHEADER)hDIB;

*lpbi=bi;

GetDIBits(hDC,(HBITMAP)bitmap.GetSafeHandle(),0L,(DWORD)bi.biHeight,(LPBYTE)NULL,(LPBITMAPINFO)lpbi,(DWORD)DIB_RGB_COLORS);

bi=*lpbi;

if(bi.biSizeImage==0)

{

bi.biSizeImage=((((bi.biWidth*bi.biBitCount)+31)& ~31)/8)*bi.biHeight;

if(dwCompression!=BI_RGB)

bi.biSizeImage=(bi.biSizeImage*3)/2;

}

dwLen+=bi.biSizeImage;

if(handle=GlobalReAlloc(hDIB,dwLen,GMEM_MOVEABLE))

hDIB=handle;

else

{

GlobalFree(hDIB);

SelectPalette(hDC,hPal,FALSE);

::ReleaseDC(NULL,hDC);

return NULL;

}

lpbi=(LPBITMAPINFOHEADER)hDIB;

BOOL bGotBits=GetDIBits(hDC,(HBITMAP)bitmap.GetSafeHandle(),0L,(DWORD)bi.biHeight,(LPBYTE)lpbi+(bi.biSize+nColors*sizeof(RGBQUAD)),

(LPBITMAPINFO)lpbi,(DWORD)DIB_RGB_COLORS);

if(!bGotBits)

{

GlobalFree(hDIB);

SelectPalette(hDC,hPal,FALSE);

::ReleaseDC(NULL,hDC);

return NULL;

}

SelectPalette(hDC,hPal,FALSE);

::ReleaseDC(NULL,hDC);

return hDIB;

}

BOOL CTestestView::WriteDIB(LPTSTR szFile, HANDLE hDIB)

{

BITMAPFILEHEADER hdr;

LPBITMAPINFOHEADER lpbi;

if(!hDIB)

return FALSE;

CFile file;

if(!file.Open(szFile,CFile::modeWrite|CFile::modeCreate))

return FALSE;

lpbi=(LPBITMAPINFOHEADER)hDIB;

int nColors=1<<lpbi->biBitCount;

hdr.bfType=((WORD)('M'<<8)|'B');

hdr.bfSize=::GlobalSize(hDIB)+sizeof(hdr);

hdr.bfReserved1=0;

hdr.bfReserved2=0;

hdr.bfOffBits=(DWORD)(sizeof(hdr)+lpbi->biSize+nColors*sizeof(RGBQUAD));

file.Write(&hdr,sizeof(hdr));

file.Write(lpbi,GlobalSize(hDIB));

return TRUE;

}

在保存时调用函数WriteWindowToDIB()

void CTestestView::OnFileSave()

{

// TODO: Add your command handler code here

WriteWindowToDIB("D:\\My.bmp",this);

}

本人测试过,确实直接加上就可以了,但是注意类名要改为和你的一致的

以上转载声明:http://wmnmtm.blog.163.com/blog/static/38245714200963032449/

二:VC实现自绘图形输出到bmp文件

在用vc做程序时候,经常需要把输出的文本和图形保存到位图文件,当然可以有现成的控件来实现,但总不能如自己所愿,还是自己动手写吧!如下图:

一、实现方法

  要把文本和图形保存到位图文件,只要对掌握位图结构有一定的了解,一切都ok呢。先必须要创建内存设备环境,然后内存设备环境创建的DIB区域,别忘了还要创建个CBitmap对象,CBitmap对象必须和DIB区域关联起来,把CBitmap对象选择到当前设备环境,然后在当前设备环境输出文本和图形就可以了。

二、具体实现代码如下

void CTestSaveBmpView::SaveAsBmp(CString filename)

{

//定义图形大小

int iWidth = 800;

int iHeight = 600;

int iPixel = 16;

//图形格式参数

LPBITMAPINFO lpbmih = new BITMAPINFO;

lpbmih->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);

lpbmih->bmiHeader.biWidth = iWidth;

lpbmih->bmiHeader.biHeight = iHeight;

lpbmih->bmiHeader.biPlanes = 1;

lpbmih->bmiHeader.biBitCount = iPixel;

lpbmih->bmiHeader.biCompression = BI_RGB;

lpbmih->bmiHeader.biSizeImage = 0;

lpbmih->bmiHeader.biXPelsPerMeter = 0;

lpbmih->bmiHeader.biYPelsPerMeter = 0;

lpbmih->bmiHeader.biClrUsed = 0;

lpbmih->bmiHeader.biClrImportant = 0;

//创建位图数据

HDC hdc,hdcMem;

HBITMAP hBitMap = NULL;

CBitmap *pBitMap = NULL;

CDC *pMemDC = NULL;

BYTE *pBits;

hdc = CreateIC(TEXT("DISPLAY"),NULL,NULL,NULL);

hdcMem = CreateCompatibleDC(hdc);

hBitMap = CreateDIBSection(hdcMem,lpbmih,DIB_PAL_COLORS,(void **)&pBits,NULL,0);

pBitMap = new CBitmap;

pBitMap->Attach(hBitMap);

pMemDC = new CDC;

pMemDC->Attach(hdcMem);

pMemDC->SelectObject(pBitMap);

//

CRect rc(0,0,iWidth,iHeight);

pMemDC->SetBkMode(TRANSPARENT);

//添加自绘图形

DrawCurve(pMemDC,rc);

//保存到文件并创建位图结构

BITMAPFILEHEADER bmfh;

ZeroMemory(&bmfh,sizeof(BITMAPFILEHEADER));

*((char *)&bmfh.bfType) = ''B'';

*(((char *)&bmfh.bfType) + 1) = ''M'';

bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);

bmfh.bfSize = bmfh.bfOffBits + (iWidth * iHeight) * iPixel / 8;

TCHAR szBMPFileName[128];

int iBMPBytes = iWidth * iHeight * iPixel / 8;

strcpy(szBMPFileName,filename);

CFile file;

if(file.Open(szBMPFileName,CFile::modeWrite | CFile::modeCreate))

{

file.Write(&bmfh,sizeof(BITMAPFILEHEADER));

file.Write(&(lpbmih->bmiHeader),sizeof(BITMAPINFOHEADER));

file.Write(pBits,iBMPBytes);

file.Close();

}

pMemDC->DeleteDC();

delete pMemDC; pMemDC = NULL;

delete pBitMap; pBitMap = NULL;

delete lpbmih; lpbmih = NULL;

}

//输出文本和图形

void CTestSaveBmpView::DrawCurve(CDC *pDC, CRect rcClient)

{

//页面背景色

CBrush brushCtl;

brushCtl.CreateSolidBrush(RGB(255,255,255));

pDC->Rectangle(rcClient);

pDC->FillRect(rcClient,&brushCtl) ;

brushCtl.DeleteObject();

CPen pen;

pen.CreatePen(PS_SOLID, 1, RGB(255,0,0));

CPen *oldPen = pDC->SelectObject(&pen);

double xMin = 10.00f, xMax = 100.00f;

double yMin = 10.00f, yMax = 200.00f;

double dbX1 = (xMax- xMin)/100 + xMin;

double dbY1 = 600/dbX1 ;

for (int i=1; i<100; i++) //曲线

{

double dbX2 = (xMax- xMin)*i/100 + xMin;

double dbY2 = 600/dbX2 ;

pDC->MoveTo(int(rcClient.left+(dbX1 - xMin)*rcClient.Width()/(xMax- xMin)),

int(rcClient.bottom-(dbY1- yMin)*rcClient.Height()/(yMax- yMin)));

pDC->LineTo(int(rcClient.left+(dbX2 - xMin)*rcClient.Width()/(xMax- xMin)),

int(rcClient.bottom-(dbY2- yMin)*rcClient.Height()/(yMax- yMin)));

dbX1=dbX2;

dbY1=dbY2;

}

pDC->SelectObject(oldPen);

pen.DeleteObject();

oldPen = NULL;

}

//保存的实现

void CTestSaveBmpView::OnRButtonDown(UINT nFlags, CPoint point)

{

CFileDialog dlg(false,NULL,NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,

"位图文件(*.bmp)|*.bmp|",

NULL);

if (dlg.DoModal()!= IDOK) return;

CString filename = dlg.GetFileName() + ".bmp";

SaveAsBmp(filename);

CView::OnRButtonDown(nFlags, point);

}

自己的实际应用时就把上面举例的画曲线的代码换成自己的画图程序就可以了,这个我自己试过,是成功的
第二部分转载声明:http://blog.csdn.net/ithomer/article/details/4991219

三:实现BMP信息的读取以及改写数据并保存

typedef struct

BITMAPFILEHEADER fileHeader;

BITMAPINFOHEADER infoHeader;

FILE* pfin =fopen("原始图像.bmp","rb");

FILE* pfout = fopen( "修改后的图像.bmp" , "wb");

//Read the Bitmap file header;

fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);

//Read the Bitmap info header;

fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);

//为简化代码,只处理24位彩色

if( infoHeader.biBitCount == 24 )

{

int size = infoHeader.biWidth*infoHeader.biHeight;

RGB img[infoHeader.biHeight][infoHeader.biWidth];//此处地方实现有问题,RGB img[][]要给定具体的图片长和宽

fread( img , sizeof(RGB) , size , pfin );

//把第50行染成黑色

int i = 0;

for( ; i < infoHeader.biWidth ; i++ )

{

img[50][i].b =img[50][i].g=img[50][i].r= 0;

}

//将修改后的图片保存到文件

fwrite( &fileHeader , sizeof(fileHeader) , 1 , pfout );

fwrite( &infoHeader , sizeof(infoHeader) , 1 , pfout );

fwrite( img , sizeof(RGB) , size , pfout );

}

fclose(pfin);

fclose(pfout);

四:得到BMP图像的像素数据

//下面这个函数式写入BMP图像的基本信息

void CSaveBmpDlg::Snapshot(BYTE *pData, int width, int height, const char *filename)

{

int size = width*height*3; // 每个像素点3个字节

// 位图第一部分,文件信息

BITMAPFILEHEADER bfh;

bfh.bfType = 0x4d42; //bm

bfh.bfSize = size // data size

+ sizeof( BITMAPFILEHEADER ) // first section size

+ sizeof( BITMAPINFOHEADER ) // second section size

;

bfh.bfReserved1 = 0; // reserved

bfh.bfReserved2 = 0; // reserved

bfh.bfOffBits = bfh.bfSize - size;

// 位图第二部分,数据信息

BITMAPINFOHEADER bih;

bih.biSize = sizeof(BITMAPINFOHEADER);

bih.biWidth = width;

bih.biHeight = height;

bih.biPlanes = 1;

bih.biBitCount = 24;

bih.biCompression = 0;

bih.biSizeImage = size;

bih.biXPelsPerMeter = 0;

bih.biYPelsPerMeter = 0;

bih.biClrUsed = 0;

bih.biClrImportant = 0;

FILE * fp = fopen( filename, "wb" );//w+

if( !fp ) return;

fwrite( &bfh, 1, sizeof(BITMAPFILEHEADER), fp );//返回值:返回实际写入的数据块数目

fwrite( &bih, 1, sizeof(BITMAPINFOHEADER), fp );

fwrite( pData, 1, size, fp );

fclose( fp );

}

//下面这个函数就是在自己建立的一个BMP图文件里输入数据

void CSaveBmpDlg::GenerateBMP()

{

int i=0, j=0;

struct {

BYTE b;

BYTE g;

BYTE r;

} pRGB[420][420]; // 定义位图数据

memset( pRGB, 0, sizeof(pRGB) ); // 设置背景为黑色

// 在中间画一个100*100的矩形

for( i=70;i<170;i++ ){

for( j=110;j<210;j++ ){

pRGB[i][j].r = 0xff;

}

}

// 生成BMP图片

Snapshot( (BYTE *)pRGB, 420, 420, "C:\\rgb.bmp" );

}

而GenerateBMP()函数里的画矩形部分可以改成:

pRGB img[420][420];

int
size = infoHeader.biWidth*infoHeader.biHeight;

fread( img , sizeof(RGB) , size , pfin );

Snapshot(
(BYTE *)pRGB, 420, 420, "C:\\rgb.bmp" );

这样就实现了你打开图形的原样输出,保存在C:\\rgb.bmp下,如果你只是要得到数据,那么像素的数据就在img这个数组里

五:BMP图片的静态控件显示

自己有尝试很多的方法,可是都觉得比较麻烦,而且尝试着得到BMP文件的数据然后在静态控件里画出来,我觉得理论上肯定是可以的,只是自己尝试未果,不过我还是不会放弃尝试的,要是弄出来了我再跟新进博客。或者有看到这篇博文的人有知道的也可以留言给我,谢谢合作!下面我将我在网上看到的比较少的代码实现打开BMP图片并显示的代码附上,大家一起学习学习

void
CSaveBmpDlg::OnOpenbmp()

{

// TODO: Add your control notification handler code here

CFileDialog dlg(true,".bmp",m_szFilePathName,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"Bitmap Files(*.bmp)|*.bmp||");

if(dlg.DoModal()==IDOK)

{

// m_szFileName=dlg.GetFileName();

m_szFilePathName=dlg.GetPathName();

}

else

return;

CStatic* opYuanTu=NULL;

opYuanTu=(CStatic*)GetDlgItem(IDC_STATIC);

ShowBmp(opYuanTu,m_szFilePathName);

}

void CSaveBmpDlg::ShowBmp(CStatic *pStatic, CString s)

{

HBITMAP hbitmap;

//pStatic=NULL;

//pStatic=(CStatic*)GetDlgItem(IDC_BMP);

hbitmap=(HBITMAP)::LoadImage(::AfxGetInstanceHandle(),s,IMAGE_BITMAP,0,0,LR_LOADFROMFILE|LR_CREATEDIBSECTION);

CBitmap hbmp;

hbmp.Attach(hbitmap);

BITMAP bm;

hbmp.GetBitmap(&bm);

CDC dcMem;

dcMem.CreateCompatibleDC(GetDC());

CBitmap *pOldBitmap=(CBitmap*)dcMem.SelectObject(hbmp);

CRect IRect;

pStatic->GetClientRect(&IRect);

pStatic->GetDC()->StretchBlt(IRect.left,IRect.top,IRect.Width(),IRect.Height(),&dcMem,0,0,bm.bmWidth,bm.bmHeight,SRCCOPY);

dcMem.SelectObject(&pOldBitmap);

}

关于第五点自己遗留的问题,经过自己的一番实验,还是让我给实现了,现在也把代码分享在下面,就是打开一张BMP图,得到它的数据后,自己将它的数据重新在编辑框上画出来,其实如果是显示的话我觉得这篇博文里有更好的方法,但是如果作为测试图片数据,尤其是在只有图像数据的时候还是比较有实际的用途的,代码很简单:基本思想就是遍历数据数组,在里面找满足每个条件的数据,然后得到这些像素值对应的坐标,再将其画出来就可以了

BITMAPFILEHEADER
fileHeader;

BITMAPINFOHEADER infoHeader;

FILE* pfin =fopen("D:\\ztlk.bmp","rb");

//Read the Bitmap file header;

fread(&fileHeader,sizeof(BITMAPFILEHEADER),1,pfin);

//Read the Bitmap info header;

fread(&infoHeader,sizeof(BITMAPINFOHEADER),1,pfin);

RGB img[420][420];

//为简化代码,只处理24位彩色

if( infoHeader.biBitCount == 24 )

{

int size = infoHeader.biWidth*infoHeader.biHeight;

fread( img , sizeof(RGB) , size , pfin );

}

/* typedef struct{

int x;

int y;

}point;*/

point zuobiao,Staticpoint; //point 如上定义的一个结构体

CPen pen(PS_SOLID,2,RGB(255,0,0));

CRect rect;

CDC *pDC =GetDlgItem(IDC_STATIC)->GetDC();

CPen* pOldPen=pDC->SelectObject(&pen);

GetDlgItem(IDC_STATIC)->GetClientRect(&rect);

float f,f1;

f=rect.Width()/420.0;

f1=rect.Height()/420.0;

for(int i=infoHeader.biHeight;i>0;i--)

for(int j=infoHeader.biWidth;j>0;j--)

{

if(img[i][j].b==0&&img[i][j].g==0&&img[i][j].r==0)

{

zuobiao.x=j;

zuobiao.y=i;

Staticpoint.x=zuobiao.x*f;

Staticpoint.y=zuobiao.y*f1;

pDC->MoveTo(Staticpoint.x,rect.Height()-Staticpoint.y);

pDC->LineTo(Staticpoint.x,rect.Height()-Staticpoint.y);

}

}

pDC->DeleteDC();

fclose(pfin);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: