MFC透明位图(2)--PNG的实现

 

类CImage,头文件atlimage.h

1.显示背景透明的PNG图片

CImage m_Image;

m_Image.Load(图片路径);//装载图片

for(int i = 0; i < m_Image.GetWidth(); i++)

{

    for(int j = 0; j < m_Image.GetHeight(); j++)

    {

        unsigned char* pucColor = reinterpret_cast<unsigned char *>(m_Image.GetPixelAddress(i , j));

        pucColor[0] = pucColor[0] * pucColor[3] / 255;

        pucColor[1] = pucColor[1] * pucColor[3] / 255;

        pucColor[2] = pucColor[2] * pucColor[3] / 255;

    }

}

m_Image.Draw(...)显示图片

BOOL CImage::Draw( HDC hDestDC, int xDest, int yDest, int nDestWidth,

 int nDestHeight, int xSrc, int ySrc, int nSrcWidth, int nSrcHeight )

 

2.显示整体半透明PNG图片

BOOL CImage::AlphaBlend( HDC hDestDC, int xDest, int yDest,

 int nDestWidth, int nDestHeight, int xSrc, int ySrc, int nSrcWidth,

 int nSrcHeight, BYTE bSrcAlpha, BYTE bBlendOp )

bSrcAlpha=255表示图片整体不透明

bSrcAlpha=0表示整体透明

 

备注:

typedef struct _BLENDFUNCTION {  

      BYTE     BlendOp;  

      BYTE     BlendFlags;  

      BYTE     SourceConstantAlpha;  

      BYTE     AlphaFormat;  

    }BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;

BlendOp:为AC_SRC_OVER

BlendFlags:为保留项,为0

SourceConstantAlpha:为图片整体的不透明度,如果要使用图片像素自身的Alpha值,则要将这个参数设置为255

AlphaFormat:如果使用SourceConstantAlpha作为描画图片的整体不透明度,则为设置为0,如果使用图片像素自身的Alpha值,则设置为AC_SRC_ALPHA.将AlphaFormat设置为AC_SRC_ALPHA.MSDN对这种情况下颜色混合的计算方法作了描述,如下:

Dst.Red = Src.Red + (1 - Src.Alpha) * Dst.Red  

Dst.Green = Src.Green + (1 - Src.Alpha) * Dst.Green  

Dst.Blue = Src.Blue + (1 - Src.Alpha) * Dst.Blue 

Src是指我们要描画的图片,Dst是指目标DC的上下文,Src.Alpha应该不是像素的Alpha值,而应该是Alpha/255;按照这个公式,我们可以举个例子计算一下:Src上一个像素为RGB(255, 255, 255),Alpha值为0,与之混合的Dst上相应像素为RGB(128, 128, 128),混合后得出的结果为:

R = 255 + (1 - 0 / 255) * 128;

G = 255 + (1 - 0 / 255) * 128;

B = 255 + (1 - 0 / 255) * 128;

计算结果大于255,函数内部自动将其设置为255,最后为RGB(255, 255, 255),仍然为白色.而当Src中像素的颜色为RGB(0, 0, 0),则结果为Dst的颜色RGB(128, 128, 128),实现了透明效果.按照这个公式计算,很多颜色的半透明或透明效果都无法实现.

参考MSDN上在SourceConstantAlpha不等于255时的混合计算公式,我们可以将公式修改为

view plaincopy to clipboardprint?

Dst.Red = Src.Red * Src.Alpha + (1 - Src.Alpha) * Dst.Red  

Dst.Green = Src.Green * Src.Alpha + (1 - Src.Alpha) * Dst.Green  

Dst.Blue = Src.Blue * Src.Alpha + (1 - Src.Alpha) * Dst.Blue

 

源文档 <http://blog.sina.com.cn/s/blog_510a3ce00100gd3v.html>

 

 

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

最后的解决方法,写一个小程序对PNG图片每个象素进行预乘运算,然后保存成PNG图片,实际效果良好。

I overwrote CImage::Load() and now everything works fine:

 

HRESULT CImg::Load(LPCTSTR pszFileName, bool bPreMultiply)

{

 HRESULT retRes = CImage::Load(pszFileName);

 if(!IsNull() && bPreMultiply)

 {

  unsigned char * pCol = 0;

  long lW = GetWidth();

  long lH = GetHeight();

  for(long ixy = 0; ixy < lH; ixy ++)

  {

   for(long ixx = 0; ixx < lW; ixx ++)

   {

    pCol = (unsigned char *)GetPixelAddress(ixx,ixy);

    unsigned char alpha = pCol[3];

    if(alpha < 255)

    {

    pCol[0] = ((pCol[0] * alpha) + 127) / 255;

    pCol[1] = ((pCol[1] * alpha) + 127) / 255;

    pCol[2] = ((pCol[2] * alpha) + 127) / 255;

    }

   }

  }

 }

 return retRes;

}

PS:最后发现有一些png图的透明色可以被正常处理,现在怀疑是这些图片的导出方式不对导致。

 

源文档 <http://blog.csdn.net/mergerly/archive/2009/10/21/4710676.aspx>

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是将一张PNG图片转换为单色位图MFC代码: ```cpp CImage pngImage; pngImage.Load(_T("image.png")); // 载入PNG图片 // 获取图片尺寸 int width = pngImage.GetWidth(); int height = pngImage.GetHeight(); // 创建单色位图 CBitmap bmp; bmp.CreateBitmap(width, height, 1, 1, NULL); // 获取位图DC CDC* pDC = CDC::FromHandle(::GetDC(NULL)); CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap* pOldBitmap = memDC.SelectObject(&bmp); // 将PNG图片绘制到位图pngImage.BitBlt(memDC, 0, 0); // 将位图转换为单色位图 BITMAP bm; bmp.GetBitmap(&bm); int size = bm.bmWidthBytes * bm.bmHeight; BYTE* pBits = new BYTE[size]; memset(pBits, 0, size); GetBitmapBits(bmp, size, pBits); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { int index = y * bm.bmWidthBytes + x / 8; int bit = x % 8; COLORREF color = GetPixel(memDC, x, y); if (GetRValue(color) > 128) // 如果像素点亮度大于128,则设置为白色 { pBits[index] |= (0x80 >> bit); } } } // 更新位图数据 SetBitmapBits(bmp, size, pBits); // 清空内存 delete[] pBits; // 恢复DC状态 memDC.SelectObject(pOldBitmap); ::ReleaseDC(NULL, pDC->GetSafeHdc()); ``` 以上代码使用了MFC的一些基本函数,包括CImage类、CDC类、CBitmap类和一些GDI函数,主要实现了以下步骤: 1. 载入PNG图片; 2. 创建单色位图; 3. 获取位图DC,并将PNG图片绘制到位图上; 4. 将位图转换为单色位图; 5. 更新位图数据; 6. 清空内存; 7. 恢复DC状态。 其中,第4步是将位图每个像素点的颜色值转换为单色位图的关键步骤,根据像素点的亮度值,将其设置为白色或黑色。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值