好久都没有写博客了,因为开发进入到白热化阶段,就发现没有多少时间可以用来写博客了。。

      不过还是要把这个小技术点记录一下,因为困扰了我好久,终于在codeguru上找到了解决办法。

      在MFC中,在使用CImage类(vs2008及以上)的时候,读取PNG图片并显示,实际上并不是透明的,这里是微软的一个BUG,他对透明的处理就是“白色”。先来做一个实验,为了让结果比较明显,把对话框的背景设为一个便于区别的颜色。

      建立一个新的对话框工程,背景色设为RGB(255, 202, 138)。估计还有很多人不知道肿么设置对话框的背景色,很简单,在OnInitDialog()中添加这句代码即可:

 
  
  1. this->SetBackgroundColor(RGB(255,202,138));  

      测试的方法就是点击鼠标后画上一个小PNG图片。添加鼠标抬起事件:

 
  
  1. //CpngTestDlg.h 
  2. CImage m_pngImage; 
  3.  
  4. //CpngTestDlg.cpp 
  5. BOOL CpngTestDlg::OnInitDialog() 
  6.         ... 
  7.         m_pngImage.Load(_T("c:\\flag.png")); 
  8. void CpngTestDlg::OnLButtonUp(UINT nFlags, CPoint point) 
  9.     // TODO: Add your message handler code here and/or call default 
  10.     CDC * pDC = GetDC(); 
  11.     m_pngImage.Draw(pDC->GetSafeHdc(), point.x, point.y); 
  12.  
  13.     CDialogEx::OnLButtonUp(nFlags, point); 

好了,运行,在对话框里随便点一下,可以看到,效果并不是我们想要的。

当时遇见这个问题,第一个想的是,是不是图片的问题?后来在PS中查看了一下,排除了Alpha通道的问题。会不会是CImage类的问题?是不是需要对透明图片进行额外的设置?带着这几个问题,去网上找了些资料。很多解决方案都是使用TransparentBlt(),设定关键色为白色,来绘制图片。我试了一下,没有效果,不知道他们是如何成功的。

最终在codeguru上找到了解决方案,现在那个帖子已经不在了,不知道为什么。。

先来看看造成这种现象的原因是什么:

      PNG图片的透明背景总是一片白色,后来才发现这其实是微软GDI+的设计问题, PNG图片是ARGB,使用GDI+载入图片的时候,GDI+会默认已经进行了预剩运算(PARGB), 即每象素的实际值是已经和ALPHA值按比例相乘的结果,实际上它根本就没有做预乘, 在使用透明图片的象素ALPHA通道的时候,CImage内部正是调用的AlphaBlend, 没有预乘的图当作预乘的图片处理的结果就是这相当于一张和纯白背景进行了预剩, 所以图象总是出现白色背景。

 然后对症下药,在载入图像之后,对其进行处理:

 
  
  1. for(int i = 0; i < m_pngImage.GetWidth(); i++)   
  2. {   
  3.     for(int j = 0; j < m_pngImage.GetHeight(); j++)   
  4.     {   
  5.         unsigned char* pucColor = reinterpret_cast<unsigned char *>(m_pngImage.GetPixelAddress(i , j));   
  6.         pucColor[0] = pucColor[0] * pucColor[3] / 255;   
  7.         pucColor[1] = pucColor[1] * pucColor[3] / 255;   
  8.         pucColor[2] = pucColor[2] * pucColor[3] / 255;   
  9.     }   

 然后再看看结果: