GDI+图像处理学习笔记——改变图像的透明度

来源:岁月联盟 编辑:exp 时间:2012-02-17

GDI+是WindowsXp和windows Server 2003中的一个子系统,主要用于处理系统的绘制消息。GDI+是GDI的扩展,其继承了GDI的有点并在其基础上进行了改进,包括增加了一些GDI无法绘制的图形函数,同时GDI+重新设计了编程模型,使开发图形程序更加方便。作为图形设备接口的GDI+使得应用程序开发人员在输出屏幕和打印机信息的时候无需考虑具体显示设备的细节,他们只需调用GDI+库输出的类的一些方法即可完成图形操作,真正的绘图工作由这些方法交给特定的设备驱动程序来完成,GDI+使得图形硬件和应用程序相互隔离.从而使开发人员编写设备无关的应用程序变得非常容易。

            在GDI+中,颜色的表示除了红绿蓝三种分量外,增加了一种叫做Alpha的变量,其作用是用来表示颜色的透明度,取值范围为0-255.本例中实现的图像透明度的改变主要就是改变图像中每一种颜色的Alpha的值来达到改变透明度的效果。

       本例中在VS2010中开发,具体效果如下

 /

  打开图片文件,默认透明度为0,相应的alpha值为1,单击色彩处理菜单项,打开图像透明度对话框,改变滑动块的值后,图片透明度相应改变  

/

 

下面详细介绍具体实现过程:


vs2010中GDI+的配置 
          根据本人实测,在VS2010中只需在资源文件stdafx.h中包含进Gdiplus.h头文件中就可以了,并不需要在项目属性中添加GDI+库文件,左飞编写的《数字图像处理-原理与实践》中说的在VS2005下好像要在项目属性->配置属性->链接器->输入->附加依赖项中添加gdiplus.lib。本人机器上没有VS2005,所以也不敢妄下结论。不过在VS2010下只需在stdafx.h中添加如下代码即可
         [cpp] #include <GdiPlus.h>  
using namespace Gdiplus; 
#include <GdiPlus.h>
using namespace Gdiplus;     2.改变图像透明度的原理
        之前说过,在GDI+中增加了一个alpha的变量用于表示图像的透明度,可是由于在GDI+中不能直接改变图像中的alpha的值(本人也是初学GDI+没多久,这句对与否暂时不好说),因此我们要用到一个叫做ColorMatrix的结构体,他是一个5*5的数组,我们用其表示颜色的变换关系。下面是GDI+中ColorMatrix的定义
[cpp] typedef struct{ 
     REAL m[5][5]; 
}ColorMatrix; 
typedef struct{
     REAL m[5][5];
}ColorMatrix;         其中REAL类型我在微软的MSDN中并没有查到具体定义,《数字图像处理-原理与实践》一书中,说其表示的一个单精度的浮点类型。
         下面说一下上面提到的ColorMatrix,下面是GDI+下的一个颜色变换公式        


                                              1  0  0  0  0
                                                             0   1   0   0   0

        [R  G  B A 1]=[r  g  b  a  1] 0   0   1   0   0

                                                             0   0   0   1   0

                                                             0   0   0   0   1

        解释下,R/G/B/A表示颜色变换后的红、绿、蓝和透明度的分量,r/g/b/a表示颜色变换前图像的红、绿、蓝和透明度的分量,后面是一个5*5的矩阵,这个矩阵就是ColorMatrix结构体的5*5数组。根据矩阵的乘法,我们要改变图像的透明度,只需定义一个ColorMatrix结构体,将第四行第四列的数改成相应的透明度即可。

   3.图像透明度对话框实现

      对话框很简单,添加两个控件一个是编辑框,给其绑定变量m_alpha,类型为REAL,另一个是滑动块控件,然后在主对话框类重载消息WM_HSCROLL,重载后会在该对话框类自动生成一个OnHScroll的消息响应函数,我们主要的操作就是在这个函数类实现,下面是该函数类的代码

 

[cpp] BOOL CImageDlg::OnInitDialog() 

    CDialogEx::OnInitDialog(); 
    CSliderCtrl* pSlider; 
    pSlider=(CSliderCtrl*)GetDlgItem(IDC_SLIDER_ALPHA);//根据控件ID绑定控件  
    pSlider->SetRange(0,100);//设置滑动块活动范围  
    // TODO:  在此添加额外的初始化      
 
    return TRUE;  // return TRUE unless you set the focus to a control  
    // 异常: OCX 属性页应返回 FALSE  

BOOL CImageDlg::OnInitDialog()
{
 CDialogEx::OnInitDialog();
 CSliderCtrl* pSlider;
 pSlider=(CSliderCtrl*)GetDlgItem(IDC_SLIDER_ALPHA);//根据控件ID绑定控件
 pSlider->SetRange(0,100);//设置滑动块活动范围
 // TODO:  在此添加额外的初始化 

 return TRUE;  // return TRUE unless you set the focus to a control
 // 异常: OCX 属性页应返回 FALSE
}
    [cpp] void CImageDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
 

    // TODO: 在此添加消息处理程序代码和/或调用默认值..........................................  
        CSliderCtrl*pSlider=(CSliderCtrl*)pScrollBar; 
    m_alpha=(100.0-pSlider->GetPos())/100.0; 
    UpdateData(FALSE); 
void CImageDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)

{
 // TODO: 在此添加消息处理程序代码和/或调用默认值..........................................
        CSliderCtrl*pSlider=(CSliderCtrl*)pScrollBar;
 m_alpha=(100.0-pSlider->GetPos())/100.0;
 UpdateData(FALSE);[cpp] //获取View视图类的指针并刷新视图 
         //获取View视图类的指针并刷新视图[cpp]     CMainFrame *pMainFrame=(CMainFrame*)AfxGetMainWnd(); 
    CGdiPlus_ColorView *pView=(CGdiPlus_ColorView*)pMainFrame->GetActiveView(); 
    //pView->UpdateWindow();  
    pView->Invalidate(); 
    //UpdateWindow();     
    CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar); 

 CMainFrame *pMainFrame=(CMainFrame*)AfxGetMainWnd();
 CGdiPlus_ColorView *pView=(CGdiPlus_ColorView*)pMainFrame->GetActiveView();
 //pView->UpdateWindow();
 pView->Invalidate();
 //UpdateWindow(); 
 CDialogEx::OnHScroll(nSBCode, nPos, pScrollBar);
}   4.Doc文档类中的相关操作
      添加变量image,用以保存打开的图像文件,另重载文档类中的OnOpenDocument函数
     [cpp] public: 
    virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); 
    Image* image; 
public:
 virtual BOOL OnOpenDocument(LPCTSTR lpszPathName);
 Image* image;      在OnOpenDocument函数中将打开的图像文件存入Image变量中
      [cpp] BOOL CGdiPlus_ColorDoc::OnOpenDocument(LPCTSTR lpszPathName) 

    if (!CDocument::OnOpenDocument(lpszPathName)) 
        return FALSE; 
     // TODO:  在此添加您专用的创建代码  
     image=Image::FromFile(lpszPathName);     
      //UpdateAllViews(NULL);  
     return TRUE; 

BOOL CGdiPlus_ColorDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
 if (!CDocument::OnOpenDocument(lpszPathName))
  return FALSE;
  // TODO:  在此添加您专用的创建代码
  image=Image::FromFile(lpszPathName); 
   //UpdateAllViews(NULL);
  return TRUE;
}


   5.View视图类中的相关操作

      首先要添加两个变量,1个时alpha的值,另一个是指向图像透明度对话框的指针;

     

[cpp] public:  
    REAL alpha;//图像透明度  
    CImageDlg *dlg;//图像透明度对话框指针  
public: 
 REAL alpha;//图像透明度
 CImageDlg *dlg;//图像透明度对话框指针 

       然后在资源文件中给菜单栏添加图像处理这个菜单项,并为其绑定时间处理程序,在时间处理程序中用非模态方式显示图像透明度对话框

      

[cpp] void CGdiPlus_ColorView::OnImageAlpha() 

    // TODO: 在此添加命令处理程序代码     
    dlg->Create(IDD_DIALOG1,this); 
    dlg->ShowWindow(SW_SHOW);//非模态对话框  

void CGdiPlus_ColorView::OnImageAlpha()
{
 // TODO: 在此添加命令处理程序代码 
 dlg->Create(IDD_DIALOG1,this);
 dlg->ShowWindow(SW_SHOW);//非模态对话框
}     最后在OnDraw函数中实现绘图操作

    

[cpp] void CGdiPlus_ColorView::OnDraw(CDC* pDC) 
{    
    CGdiPlus_ColorDoc* pDoc = GetDocument(); 
    ASSERT_VALID(pDoc); 
    if (!pDoc) 
        return; 
    if (pDoc->image==NULL) 
    { 
        return; 
    }    
    alpha=dlg->m_alpha; 
void CGdiPlus_ColorView::OnDraw(CDC* pDC)
{  
 CGdiPlus_ColorDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 if (!pDoc)
  return;
 if (pDoc->image==NULL)
 {
  return;
 } 
 alpha=dlg->m_alpha;[cpp]        //获得图像的宽度和高度  
int nWidth=pDoc->image->GetWidth(); 
int nHight=pDoc->image->GetHeight(); 
       //声明空白图像  
Bitmap bitmap(nWidth,nHight); 
Rect rect(0,0,nWidth,nHight); 
 
Graphics temp(&bitmap);//对新图像进行绘制  
       
ColorMatrix colorMartrix={ 
    1,0,0,0,0, 
    0,1,0,0,0, 
    0,0,1,0,0, 
       0,0,0,alpha,0, 
       0,0,0,0,1}; 
ImageAttributes imageAttr; 
imageAttr.SetColorMatrix(&colorMartrix); 
temp.DrawImage(pDoc->image,rect,0,0,nWidth,nHight,UnitPixel,&imageAttr); 
 
//创建于设备兼容的内存环境  
CDC memDC; 
CBitmap MemBitmap; 
CRect ClientRect; 
GetClientRect(ClientRect); 
memDC.CreateCompatibleDC(NULL); 
MemBitmap.CreateCompatibleBitmap(pDC,ClientRect.Width(),ClientRect.Height()); 
memDC.SelectObject(MemBitmap); 
memDC.FillSolidRect(0,0,ClientRect.Width(),ClientRect.Height(),RGB(255,255,255)); 
Graphics graph(memDC.GetSafeHdc()); 
//Graphics graph(pDC->GetSafeHdc());//获得当前设备的句柄        
// graph.DrawImage(pDoc->image,0,0);  
graph.DrawImage(&bitmap,0,0); 
pDC->BitBlt(0,0,nWidth,nHight,&memDC,0,0,SRCCOPY); 
 
// TODO: 在此处为本机数据添加绘制代码  
        //获得图像的宽度和高度
 int nWidth=pDoc->image->GetWidth();
 int nHight=pDoc->image->GetHeight();
        //声明空白图像
 Bitmap bitmap(nWidth,nHight);
 Rect rect(0,0,nWidth,nHight);

 Graphics temp(&bitmap);//对新图像进行绘制
      
 ColorMatrix colorMartrix={
  1,0,0,0,0,
  0,1,0,0,0,
  0,0,1,0,0,
        0,0,0,alpha,0,
        0,0,0,0,1};
 ImageAttributes imageAttr;
 imageAttr.SetColorMatrix(&colorMartrix);
 temp.DrawImage(pDoc->image,rect,0,0,nWidth,nHight,UnitPixel,&imageAttr);

 //创建于设备兼容的内存环境
 CDC memDC;
 CBitmap MemBitmap;
 CRect ClientRect;
 GetClientRect(ClientRect);
 memDC.CreateCompatibleDC(NULL);
 MemBitmap.CreateCompatibleBitmap(pDC,ClientRect.Width(),ClientRect.Height());
 memDC.SelectObject(MemBitmap);
 memDC.FillSolidRect(0,0,ClientRect.Width(),ClientRect.Height(),RGB(255,255,255));
 Graphics graph(memDC.GetSafeHdc());
 //Graphics graph(pDC->GetSafeHdc());//获得当前设备的句柄  
 // graph.DrawImage(pDoc->image,0,0);
 graph.DrawImage(&bitmap,0,0);
 pDC->BitBlt(0,0,nWidth,nHight,&memDC,0,0,SRCCOPY);
 
 // TODO: 在此处为本机数据添加绘制代码
}        这里面用了两种绘图方式,一种是直接向当前设备上下文输出图像,代码中注释部分。另一种是先创建与设备相兼容的内存环境,将图片先绘制在此内存环境中再拷贝到屏幕,之所以采用此种方法是因为这样可以去除视图在不断刷新过程中带来的屏幕闪烁,可是本人实际的运行效果,在不断移动滑块的时候,视图刷新依然有很明显的闪烁,本人也添加了视图类中的WM_ERASEBKGN消息,如下,但是依然不能解决其闪烁问题。


[cpp] BOOL CGdiPlus_ColorView::OnEraseBkgnd(CDC* pDC) 

    // TODO: 在此添加消息处理程序代码和/或调用默认值  
    //return TRUE;  
    return CView::OnEraseBkgnd(pDC); 


摘自 txg703003659的专栏