我之前写了一篇文章,关于如果将HBITMA(如Excel文件的图标)显示在界面上。
由于有的HBITMAP有透明色,如果直接把这个HBITMAP画在界面上,透明的地方就会显示成黑色(也不一定是黑色)。当时我用的方法是把这个HBITMAP通过转换成GDI+的Image里面,然后把这个Image画在界面上,这样就可以去掉黑色。最近又发现了一个新的方法,主要的API就是AlphaBlend函数。
先看看效果图:
图一:直接用BitBlt把HBITMAP画到DC上面
图二:通过AlphaBlend将HBITMAP画到DC上面
关于AlphaBlend,MSDN上面的解释是:
The AlphaBlend function displays bitmaps that have transparent or semitransparent pixels.
BOOL AlphaBlend(
HDC hdcDest, // handle to destination DC
int nXOriginDest, // x-coord of upper-left corner
int nYOriginDest, // y-coord of upper-left corner
int nWidthDest, // destination width
int nHeightDest, // destination height
HDC hdcSrc, // handle to source DC
int nXOriginSrc, // x-coord of upper-left corner
int nYOriginSrc, // y-coord of upper-left corner
int nWidthSrc, // source width
int nHeightSrc, // source height
BLENDFUNCTION blendFunction // alpha-blending function
);
这里最关键的就是设置这个BLENDFUNCTION 函数了,它告诉了API如何混合Alpha。关于Alpha是怎么混合的,MSDN上面有详细说明
我写的代码如下:
case WM_PAINT:
{
RECT rc = { 0 };
GetClientRect(hWnd, &rc);
int x = (rc.right - 256) / 2; // 画的图片在窗体中间
int y = (rc.bottom - 256) / 2; // 画的图片在窗体中间
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HDC hMemDC = CreateCompatibleDC(NULL); // 创建一个兼容内存DC
HGDIOBJ hOldObj = SelectObject(hMemDC, g_hBitmap); // 将g_bitmap选入到这个内存DC
// 如果用这句话的话,就会得到图一的效果
//BitBlt(hdc, x, y, ICON_WIDTH, ICON_HEIGHT, hMemDC, 0, 0, SRCCOPY);
BLENDFUNCTION ftn = { 0 };
ftn.BlendOp = AC_SRC_OVER; // 目前只能设置这个值
ftn.AlphaFormat = AC_SRC_ALPHA; // 也只能设置这个值
ftn.BlendFlags = 0; // 必须为0
ftn.SourceConstantAlpha = 255; // 指定源图片的alpha
// 调用这个函数来进行Alpha混合
AlphaBlend(hdc, x, y, 256, 256, hMemDC, 0, 0, 256, 256, ftn);
SelectObject(hMemDC, hOldObj);
DeleteObject(hMemDC);
EndPaint(hWnd, &ps);
}
break;
如果把BLENDFUNCTION 的 SourceConstantAlpha 不设置为255,那么源HBITMAP是有一个透明度的,比如我把这个值设置为128后,得到的效果图如下:
图三:SourceConstantAlpha设置为128的效果
==================================================================
上面提取的图标的大小是256 * 256。
关于如何提取一个文件的图标,方法如下:
HBITMAP ExtractFileIcon(LPCTSTR pszPath, UINT nWidth, UINT nHeight)
{
HBITMAP hBitmpa = NULL;
if ( (NULL != pszPath) && (nWidth > 0.0) && (nHeight > 0.0) )
{
IShellItemImageFactory *psif = NULL;
SIZE size = { nWidth, nHeight };
HRESULT hr = ::SHCreateItemFromParsingName(pszPath, NULL, IID_PPV_ARGS(&psif));
if ( SUCCEEDED(hr) && (NULL != psif) )
{
psif->GetImage(size, SIIGBF_ICONONLY, &hBitmpa);
}
SAFE_RELEASE(psif);
}
return hBitmpa;
}