处理bmp图像的程序框架

这是一个基于对话框的mfc程序,程序名为moduleForProcess。(说出程序的名字,只是为将来叙述方便一些)

---------------------------------
首先,在moduleForProcessDlg.h 里面写上#define DIBFILETYPE ((WORD)('M'<<8)|'B'),然后,还要给CmoduleForProcessDlg类添加一些数据成员和函数成员。具体如下:

// moduleForProcessDlg.h : 头文件
//

#pragma once

//----------------------------------
//some define
//
#define DIBFILETYPE ((WORD)('M'<<8)|'B')


// CmoduleForProcessDlg 对话框
class CmoduleForProcessDlg : public CDialog
{
// 构造
public:
 CmoduleForProcessDlg(CWnd* pParent = NULL); // 标准构造函数

// 对话框数据
 enum { IDD = IDD_MODULEFORPROCESS_DIALOG };

 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持


// 实现
protected:
 HICON m_hIcon;

 // 生成的消息映射函数
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 DECLARE_MESSAGE_MAP()
public:
 afx_msg void OnBnClickedOpenorg();

 //------------------------------------------------------------
 //data member
 //
public:
 LPSTR m_lpDibbitsOrg;//the place of original bits
 LPSTR m_lpDibbitsChg;//the place of changed bits

 HANDLE m_hDIBOrg;//the HGLOBAL of the memory where the original bits put in
 HANDLE m_hDIBChg;//the HGLOBAL of the memory where the changed bits put in

 BITMAPINFOHEADER m_bmiHeader;

 DWORD m_size;//the size of the data

 int m_Width;//the width of the bitmap,to use it ,just handy
 int m_Height;//the height of the bitmap,to use it ,just handy
 DWORD m_bytePerLine;//the real numbers of bits in one line,just for handy
 DWORD m_byteSpacePerLine;//the non use number of bits in one line,just for handy

 //----------------------------------------------------------
 //function member
 void ShowOrgImg(void);//show the original image
 void ShowChgImg(void);//show the image which has changed

//下面的函数是用向导自动生成的

 afx_msg void OnBnClickedSavechg();
 afx_msg void OnBnClickedShowchaned();
 afx_msg void OnBnClickedProcess();
};

----------------------------------------------------------------------

现在是在moduleForProcessDlg.cpp 中,下面只把改动了的地方标示出来

//记住成员函数,特别是涉及到指针的时候一定要初始化的,否则会有很大的问题

CmoduleForProcessDlg::CmoduleForProcessDlg(CWnd* pParent /*=NULL*/)
 : CDialog(CmoduleForProcessDlg::IDD, pParent),
 m_lpDibbitsChg(NULL),
 m_lpDibbitsOrg(NULL),
 m_Height(0),
 m_Width(0),
 m_byteSpacePerLine(0),
    m_bytePerLine(0)
{
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CmoduleForProcessDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // 用于绘制的设备上下文

  SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

  // 使图标在工作矩形中居中
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;

  // 绘制图标
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  if (m_lpDibbitsOrg)
  {
   ShowOrgImg();
  }
  if (m_lpDibbitsChg)
  {
   ShowChgImg();
  }
  CDialog::OnPaint();
 }
}

下面的函数实现的是打开一个bmp文件,然后,把文件中的数据保存到两个内存区域中,方便修改使用。这是一个按钮的响应函数

void CmoduleForProcessDlg::OnBnClickedOpenorg()
{
 // TODO: 在此添加控件通知处理程序代码
 CFileDialog dlg(TRUE,"bmp","*.bmp");

 if (dlg.DoModal()==IDOK)
 {
  CFile file;

  CFileException fe;

  //打开文件失败
  if(!file.Open(dlg.GetPathName(),CFile::modeRead|CFile::shareDenyWrite,&fe))
  {
   AfxMessageBox(TEXT("打开文件失败"));
   //返回
   return;
  }

  //--------------------------------------------------------
  //如果打开文件成功,则开始读文件
  //

  //step0:---------------------------------
  //如果曾经读入图片,要内存释放  
  if (m_lpDibbitsOrg)
  {
   ::GlobalUnlock(static_cast<HGLOBAL>(m_hDIBOrg));
   ::GlobalFree(static_cast<HGLOBAL>(m_hDIBOrg));
   //if the following used ,there will be wrong,don't know the reason
   //CloseHandle(m_hDIBOrg);

   m_lpDibbitsOrg=NULL;
  }

  //step1:------------------------------
  //尝试读取DIB文件头
  //
  //位图文件头
     BITMAPFILEHEADER bmfHeader;

  if(file.Read((LPSTR)&bmfHeader,sizeof(bmfHeader))!=sizeof(bmfHeader))
  {
   //大小不对
   MessageBox(TEXT("read bitmapfileheader uncorrect"));
   return ;
  }

  //判断是否是DIB对象,检查头两个字节是否是BM
  if(bmfHeader.bfType!=DIBFILETYPE)
  {
   //非DIB对象,返回NULL
   MessageBox(TEXT("Not a bitmap file!"));
   return;
  }

  //step2------------------------------------
  //read BITMAPINFOHEADER m_bmiHeader
  //
  if(file.Read((LPSTR)&m_bmiHeader,sizeof(m_bmiHeader))!=sizeof(m_bmiHeader))
  {
   //have the wrong size
   MessageBox(TEXT("Read bitmapinfoheader uncorrect!"));
   return;
  }

  //step3------------------------------------
  //read the data into the memory
  //
  m_size=static_cast<DWORD>(file.GetLength()-sizeof(BITMAPFILEHEADER)-sizeof(BITMAPINFOHEADER));

  //为data分配内存
  m_hDIBOrg=(HANDLE)::GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,m_size);
  if(m_hDIBOrg==NULL)
  {
   MessageBox(TEXT("Alloc memory failed!"));
   return;
  }
   
  //锁定DIB
  m_lpDibbitsOrg=(LPSTR)::GlobalLock((HGLOBAL)m_hDIBOrg);

  //now read the data from the file into the memory
  if(file.Read(m_lpDibbitsOrg,m_size)!=m_size)
  {
   //if don't read correct
   MessageBox(TEXT("Read the data wrong!"));
   ::GlobalUnlock((HGLOBAL)m_hDIBOrg);
   ::GlobalFree((HGLOBAL)m_hDIBOrg);
   return;
  }

  //---------------------------------------------------------------
  //now show the bitmap out on the screen
  //
  CWnd* pWnd=GetDlgItem(IDC_ORGIMG);
  CDC* theDC=pWnd->GetDC();
  CRect rect;
  pWnd->GetClientRect(&rect);

  StretchDIBits(theDC->m_hDC,
      rect.left, rect.top,
      rect.right-rect.left,
      rect.bottom-rect.top,
      0,0,
      m_bmiHeader.biWidth,
      m_bmiHeader.biHeight,
      m_lpDibbitsOrg,
      (LPBITMAPINFO)&m_bmiHeader,
      DIB_RGB_COLORS,
      SRCCOPY);


  //--------------------------------------------------------------------
  //now copy the original data into another memory play and show it out
  //

  //step0:---------------------
  //在重新分配之前一定要释放原有的内存
  //
  if (m_lpDibbitsChg)
  {
   ::GlobalUnlock(static_cast<HGLOBAL>(m_hDIBChg));
   ::GlobalFree(static_cast<HGLOBAL>(m_hDIBChg));
   //the following used ,there will be wrong,don't know the reason
   //CloseHandle(m_hDIBChg);
   
   m_lpDibbitsChg=NULL;
  }
  m_hDIBChg=(HANDLE)::GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,m_size);
  m_lpDibbitsChg=(LPSTR)::GlobalLock((HGLOBAL)m_hDIBChg);
  memcpy(m_lpDibbitsChg,m_lpDibbitsOrg,m_size);

  CWnd* pWndChg=GetDlgItem(IDC_CHGIMG);
  CDC* theDCChg=pWndChg->GetDC();
  CRect rectChg;
  pWndChg->GetClientRect(&rectChg);

  StretchDIBits(theDCChg->m_hDC,
      rectChg.left, rectChg.top,
      rectChg.right-rectChg.left,
      rectChg.bottom-rectChg.top,
      0,0,
      m_bmiHeader.biWidth,
      m_bmiHeader.biHeight,
      m_lpDibbitsChg,
      (LPBITMAPINFO)&m_bmiHeader,
      DIB_RGB_COLORS,
      SRCCOPY);
 }

 //------------------------------------------------------------
 //now just for handy,to store some date
 //
 m_Height=m_bmiHeader.biHeight;
 m_Width=m_bmiHeader.biWidth;

 m_bytePerLine=(24*m_Width+31)/32*4;
 m_byteSpacePerLine=m_bytePerLine-(24*m_Width+7)/8;

}

下面是保存内存中的图像到一幅bmp图像中去的函数,里面由于用的还是打开文件的对话框,看着有些别扭,但是总的是没有问题的

void CmoduleForProcessDlg::OnBnClickedSavechg()
{
 // TODO: 在此添加控件通知处理程序代码
 CFileDialog dlg(TRUE,"bmp","*.bmp");

 if(dlg.DoModal()==IDOK)
 {
  //write out a bmp file
  //
  HANDLE hf=CreateFile(dlg.GetPathName(),GENERIC_WRITE, FILE_SHARE_READ, NULL,
                              CREATE_ALWAYS, NULL, NULL );
  if(hf==INVALID_HANDLE_VALUE)
   return ;

  //write out the file header
  //
  BITMAPFILEHEADER bfh;
  memset(&bfh,0,sizeof(BITMAPFILEHEADER));
  bfh.bfType='MB';
  //设置位图文件的大小
  bfh.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+m_size;
  //设置位图像素所在的位置
  bfh.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

        DWORD dwWritten=0;
  WriteFile(hf,&bfh,sizeof(bfh),&dwWritten,NULL);

  //and the bitmap format
  //
  BITMAPINFOHEADER bih;
  memset(&bih,0,sizeof(BITMAPINFOHEADER));
  bih.biSize=sizeof(BITMAPINFOHEADER);
  bih.biWidth=m_Width;
  bih.biHeight=m_Height;
  bih.biPlanes=1;
  bih.biBitCount=24;

  WriteFile(hf,&bih,sizeof(bih),&dwWritten,NULL);

  //and the bits themselfs
  WriteFile(hf,m_lpDibbitsChg,m_size,&dwWritten,NULL);

  CloseHandle(hf);
 }
}

下面是对内存中的图像数据进行修改的代码,作为示例,只是很简单的把24位图像变成灰度图像。里面演示了两种获取每一点像素值的方法,其中被注释掉的是另一种方法。要注意RGB的排序问题。

void CmoduleForProcessDlg::OnBnClickedProcess()
{
 // TODO: 在此添加控件通知处理程序代码
 BYTE rColor,gColor,bColor;

 BYTE changeColor;

 //------------------------------------------
 //method 1
 //
 LPSTR lpBufferOrg=m_lpDibbitsOrg;
 LPSTR lpBufferChg=m_lpDibbitsChg;


 for (int i=0;i<m_Height;i++)
 {
  for (int j=0;j<m_Width;j++)
  {
   bColor=*lpBufferOrg;
   gColor=*(lpBufferOrg+1);
   rColor=*(lpBufferOrg+2);

   changeColor=(rColor+gColor+bColor)/3;

   *lpBufferChg=changeColor;
   *(lpBufferChg+1)=changeColor;
   *(lpBufferChg+2)=changeColor;

   lpBufferOrg+=3;
   lpBufferChg+=3;
  }
  lpBufferOrg+=m_byteSpacePerLine;
  lpBufferChg+=m_byteSpacePerLine;
 }

 /*//-------------------------------------------------------------
 //method 2
 //
 for (int i=0;i<m_Height;i++)
 {
  for (int j=0;j<m_Width;j++)
  {
   bColor=*(m_lpDibbitsOrg+(m_Height-i-1)*m_bytePerLine+j*3);
   gColor=*(m_lpDibbitsOrg+(m_Height-i-1)*m_bytePerLine+j*3+1);
   rColor=*(m_lpDibbitsOrg+(m_Height-i-1)*m_bytePerLine+j*3+2);

   changeColor=(rColor+gColor+bColor)/3;

   *(m_lpDibbitsChg+(m_Height-i-1)*m_bytePerLine+j*3)=changeColor;
   *(m_lpDibbitsChg+(m_Height-i-1)*m_bytePerLine+j*3+1)=changeColor;
   *(m_lpDibbitsChg+(m_Height-i-1)*m_bytePerLine+j*3+2)=changeColor;
  }
 }*/
}

下面是把内存中存放的数据显示出来,这里显示的是用于修改的那个内存块中的数据

void CmoduleForProcessDlg::ShowChgImg(void)
{
 CWnd* pWndChg=GetDlgItem(IDC_CHGIMG);
 CDC* theDCChg=pWndChg->GetDC();
 CRect rectChg;
 pWndChg->GetClientRect(&rectChg);

 StretchDIBits(theDCChg->m_hDC,
     rectChg.left, rectChg.top,
     rectChg.right-rectChg.left,
     rectChg.bottom-rectChg.top,
     0,0,
     m_bmiHeader.biWidth,
     m_bmiHeader.biHeight,
     m_lpDibbitsChg,
     (LPBITMAPINFO)&m_bmiHeader,
     DIB_RGB_COLORS,
     SRCCOPY);
}

下面是把内存中存放的图像的原始数据显示出来

void CmoduleForProcessDlg::ShowOrgImg(void)
{
 CWnd* pWnd=GetDlgItem(IDC_ORGIMG);
 CDC* theDC=pWnd->GetDC();
 CRect rect;
 pWnd->GetClientRect(&rect);

 StretchDIBits(theDC->m_hDC,
     rect.left, rect.top,
     rect.right-rect.left,
     rect.bottom-rect.top,
     0,0,
     m_bmiHeader.biWidth,
     m_bmiHeader.biHeight,
     m_lpDibbitsOrg,
     (LPBITMAPINFO)&m_bmiHeader,
     DIB_RGB_COLORS,
     SRCCOPY);
}

最后的两个函数都是自己定义的函数,作为辅助用的,直接从内存中读取数据然后显示到屏幕的相应区域,这样会比较的快的。

这样,很简单的,一个处理bmp图像的模板程序就完成了。如果要对图像进行别的处理,比如图像增强等等,就可以在CmoduleForProcessDlg::OnBnClickedProcess()中添加代码就可以了。



 



 

 

你可能感兴趣的:(处理bmp图像的程序框架)