Win32 透明效果相關基礎知識
Layered Windows 分層窗口。這是Windows2000開始引入的概念,重新定義了窗口的Hit Testing方法,以前都是把窗口按rectangle的方式裁剪,而把窗口加上WS_EX_LAYERED的Style后就可以根據窗口的形狀和像素 值進行Hit Testing,這樣我們的不規則窗口就變成了真正意義上的獨立窗口,而不是傳統的被一個不可見的矩形窗口所包含。
分層窗口重繪方式、透明效果產生
創建不規則窗口的三種方式
1. 通過區域相關API設置窗口的區域SetWindowRgn;;
2. 通過 SetLayeredWindowAttributes來指定特殊的透明顏色,讓背景圖的部分位置全透從而實現窗口的“不規則”;
3. 通過 UpdateLayeredWindow來指定特殊顏色透明或者根據圖片的ALPHA值來設置窗口全透。
- UpdateLayeredWindow():直接更新一個分層的窗口的位置,大小,形狀,內容和半透明度。優點是一勞永逸,不需要在窗口函數中響應各種重繪事件。
- 先用SetLayeredWindowAttributes()函數設置關於窗口透明度的信息,然后用傳統方式,在窗口函數中響應各種重繪事件。
注:分層窗口上的控件會隨着窗口一同變透明
解決方案:准備兩個窗口,窗口A和窗口B,窗口A作為顯示窗口,也就是異形窗口,而窗口B作為邏輯窗口,然后讓這兩個窗口 重疊在一塊,也可以說是在窗口B上創建了窗口A,然后通過UpdateLayeredWindow對窗口A實現異形,因為窗口A在窗口B上,那么勢必會遮 蓋住窗口B的控件,然后我們就要對窗口A通過SetWindowRgn進行裁剪,通過鏤空出控件的位置從而達到顯示出控件。
不規則區域重繪
SetWindowRgn() 函數:用於設置了一個窗口的區域.只有被包含在這個區域內的地方才會被重繪,而不包含在區域內的其他區域系統將不會顯示.
我們的不規則形狀由此而來。這個函數和它的朋友們十分強大,不僅可以定義獨立的基本形狀的區 域,還可以通過運算(CombineRgn:可對兩個區域進行交集、拷貝、相減、異或運算)來組合已有區域從而產生新的區域。
異形窗口效果示例
簡單效果
復雜效果
DUILIB庫 對透明效果的實現
DUILIB中,窗體透明度設置方法
1.設置window標簽屬性bktrans="true" alpha="200" alpha的值為0-255。這種設置是全體窗體透明度,所有控件都將變透明。
2.如果想單純設置背景透明度控件不透明度,可以制作半透明的背景圖片,設置window標簽的bktrans="true",並且不設置alpha屬性,此時背景透明,其它控件不透明。
3.單獨設置某個控件的透明度,可以使用圖片的fade屬性,或者mask屬性。fade表示設置圖片透明度,取值0-255。mask為設置透明的顏色。
DuiLib.CRenderEngine.LoadImage 對控件指定顏色mask的透明:在加載圖片資源位圖時,對顏色為mask的像素進行置零,繪制時這些像素的位置將被透明
if( *(DWORD*)(&pDest[i*4]) == mask ) {
pDest[i*4] = (BYTE)0;
pDest[i*4 + 1] = (BYTE)0;
pDest[i*4 + 2] = (BYTE)0;
pDest[i*4 + 3] = (BYTE)0;
bAlphaChannel = true;
}
圖片資源文件只加載一次,加載成功后緩存於內存中。
// data : 加載的圖片資源
if( !data ) return NULL;
if( type != NULL ) data->sResType = type;
data->dwMask = mask;
if( !m_mImageHash.Insert(bitmap, data) ) {// 緩存提速
::DeleteObject(data->hBitmap);
delete data;
data = NULL;
}
DuiLib.CRenderEngine.DrawImage 渲染引擎:透明效果渲染過程
static LPALPHABLEND lpAlphaBlend = (LPALPHABLEND) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "AlphaBlend");
.... ....
if( lpAlphaBlend && (alphaChannel || uFade < 255) ) {
// uFade:設置背景圖片透明度; alphaChannel:啟用指定顏色(Mask)透明
BLENDFUNCTION bf = { AC_SRC_OVER, 0, uFade, AC_SRC_ALPHA };
if( lpAlphaBlend == NULL ) lpAlphaBlend = AlphaBitBlt;
.... ....
// Alpha混合貼圖
lpAlphaBlend(hDC, lDestLeft, rcDest.top, lDestRight - lDestLeft, rcDest.bottom,
hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, \
lDrawWidth, rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf);
DuiLib.CControlUI.DoPaint 控件重繪過程
void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint)
{
if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return;
// 繪制循序:背景顏色->背景圖->狀態圖->文本->邊框
if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) {
CRenderClip roundClip;
// CRenderClip內部以SetWindowRgn()函數對繪制區域進行控制
CRenderClip::GenerateRoundClip(hDC, m_rcPaint, m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip);
PaintBkColor(hDC);
PaintBkImage(hDC);// 調用渲染引擎繪制背景圖片
PaintStatusImage(hDC);
PaintText(hDC);
PaintBorder(hDC);
}
else {
PaintBkColor(hDC);
PaintBkImage(hDC);
PaintStatusImage(hDC);
PaintText(hDC);
PaintBorder(hDC);
}
}
DuiLib.CPaintManagerUI.MessageHandler 各控件繪制完成后,整體粘貼到主窗口進行顯示
BLENDFUNCTION blendPixelFunction = {AC_SRC_OVER, 0, m_nOpacity, AC_SRC_ALPHA};
BOOL bRet = ::UpdateLayeredWindow(m_hWndPaint, NULL, &pt, &szWindow, m_hDcOffscreen, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA);
備課神器中,封面截圖過程中遮罩效果的實現
目標效果
A為用戶鼠標選取部分,無遮罩;
B為未選取部分,全部陰影遮罩(示例圖未完善,遮罩未覆蓋整個圖片)
B為Window窗口,設置bktrans=“true”從而實現透明;
通過SetWindowRgn裁剪,使B不繪制A區域,從而實現用戶選取效果
存在問題:A區域無法響應鼠標消息,用戶拖動操作無法響應!
改進方案
A為用戶鼠標選取部分,無遮罩;
B為未選取部分,全部陰影遮罩(示例圖未完善,未覆蓋整個圖片)
C為邏輯窗口,覆蓋整個圖片(示例圖未完善,未覆蓋整個圖片),負責用戶操作響應
C為Window窗口,設置bktrans=“true”,背景圖要求無限接近全透明,以消除到A區域的影響。
B為控件,通過背景圖產生遮罩效果
B通過SetWindowRgn裁剪,使B不繪制A區域,從而實現用戶選取效果