通过调试代码可以发现,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 = (unsigned char*)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);
}