使用CImage显示透明的PNG图片

来源:互联网 发布:pthread linux 编辑:程序博客网 时间:2024/05/17 02:49

http://blog.163.com/qcb_163/blog/static/95454664201171562130178/

 CImage 的AlphaBlend 函数可以显示半透明或透明的图片, 但是当使用这个函数显示PNG 图片时, 经常会发现PNG 图片的背景没有透明, 而是被显示为白色. 在网上多处搜索都没有找到原因, 只能自己动手了.
     通过调试代码可以发现,CImage 的AlphaBlend 函数内部调用的是全局的Window API 函数:
    AlphaBlend(HDC hdcDest,
            int nXOriginDest,
            int nYOriginDest,
            int nWidthDest,
            int hHeightDest,
            HDC hdcSrc,
            int nXOriginSrc,
            int nYOriginSrc,
            int nWidthSrc,
            int nHeightSrc,
            BLENDFUNCTION blendFunction) ;
    CImage 类在调用这个函数时, 将自己的内部DC 传递给hdcSrc, 将目标DC 传递给hdcDest.MSDN 详细描述这个函数的最后一个参数BLENDFUNCTION blendFunction.
    BLENDFUNCTION 定义如下:
    typedef struct _BLENDFUNCTION {
      BYTE     BlendOp;
       BYTE     BlendFlags;
      BYTE     SourceConstantAlpha;
       BYTE     AlphaFormat;
    }BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;
    BlendOp 总是为AC_SRC_OVER;BlendFlags 为保留项, 必须为0;SourceConstantAlpha 是图片整体的不透明度, 如果要使用图片像素自身的Alpha 值, 则要将这个参数设置为255; 最后一个参数, 如果使用SourceConstantAlpha 作为描画图片的整体不透明度, 则为设置为0, 如果使用图片像素自身的Alpha 值, 则设置为AC_SRC_ALPHA. 我们在描画带有透明效果的PNG 图片时, 要使用图片像素自身的Alpha 值, 所以要将SourceConstantAlpha 设置为255, 将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 时的混合计算公式, 我们可以将公式修改为
 


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
按照这个公式计算, 我们上面例子的结果为RGB(128, 128, 128), 可以实现透明效果.
     根据以上分析, 我们只用修改CImage 中像素的颜色, 就可以实现透明与半透明的效果了, 代码如下:
void CSample::Draw(CDC* pDC, int iX, int iY)
{
//m_stImage 为CImage 的对象
    for(int i = 0; i < m_stImage.GetWidth(); ++i)
    {
        for(int j = 0; j < m_stImage.GetHeight(); ++j
        {
            unsigned char* pucColor = m_stImage.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_stImage.AlphaBlend(pDC->m_hDC, iX, iY);       
}

原创粉丝点击