不規則視窗又叫“異形”視窗,現在遊戲的啟動器介面都是這個,像完美的《誅仙》和《口袋西遊》、金山的《劍俠》等,很有個性,非常漂亮。本文介紹如何用WTL來實現“異形視窗”。
1. 用Wizard建立一Dialog項目,設定視窗的“Border”屬性為None(即無標題列);
2. 載入位元影像:
BOOL CMainDlg :: LoadBitmap (UINT nIDResource) // nIDResource:位元影像的資源ID
{
// 載入位元影像
m_bmBitmap = new CBitmap;
if (!m_bmBitmap->LoadBitmap (nIDResource))
return FALSE;
BITMAP bm;
m_bmBitmap->GetBitmap (&bm);
int w = bm.bmWidth;
int h = bm.bmHeight;
m_bBitmapCreated = TRUE;
m_bBitmapExists = TRUE;
return TRUE;
}
3. 設定繪製地區:
void CMainDlg::MakeWindowRgn ()
{
// window rect - client rect
CRect rcWnd;
GetWindowRect (rcWnd);
CRgn rgn;
rgn.CreateRectRgn (rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom);
CRect rcClient;
GetClientRect (rcClient);
ClientToScreen (rcClient);
CRgn rgnClient;
rgnClient.CreateRectRgn (rcClient.left, rcClient.top, rcClient.right,
rcClient.bottom);
rgn.CombineRgn (HRGN(rgn), HRGN(rgnClient), RGN_XOR);
// 得到位元影像DC
CDC dcImage;
dcImage.CreateCompatibleDC (NULL);
HBITMAP hOldBmp = dcImage.SelectBitmap(*m_bmBitmap);
// 得到位元影像的寬度和高度
BITMAP bm;
m_bmBitmap->GetBitmap (&bm);
// 得到視窗的寬度和高度
CRect rc;
GetClientRect (rc);
// 得到最小的寬度和高度
int width = min (bm.bmWidth, rc.Width());
int height = min (bm.bmHeight, rc.Height());
// RLE(run-length)演算法:row_start 是找到的第一個不透明像素,一旦找到透明像素,則就會建立一直線地區。然後row_start 就變為下一個不透明像素
int row_start;
// 遍曆所有行
for (int y=0; y<height; y++)
{
// 從頭開始
row_start = 0;
int x;
// 遍曆所有列
for (x=0; x<width; x++)
{
// 若該像素是透明的
if (dcImage.GetPixel(x, y) == m_colTrans) //m_colTrans:透明色(即位元影像中這個顏色的部分是透明的)
{
// 如果還沒找到不透明的像素,則繼續搜
if (row_start == x) row_start ++;
else
{
// 已經找到一不透明線段的開始(row_start)和結尾(x),將它加到要繪製的地區裡
CRgn rgnAdd;
rgnAdd.CreateRectRgn (row_start, y, x, y+1);
rgn.CombineRgn (HRGN(rgn), HRGN(rgnAdd), RGN_OR);
row_start = x+1;
}
}
}
// 若最後一個像素仍是不透明的,則再建立一個地區
if (row_start != x)
{
CRgn rgnAdd;
rgnAdd.CreateRectRgn (row_start, y, x, y+1);
rgn.CombineRgn (HRGN(rgn), HRGN(rgnAdd), RGN_OR);
}
}
SetWindowRgn (rgn, TRUE);
}
3. 處理背景擦除
MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
LRESULT CMainDlg::OnEraseBkgnd (UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
HDC hDC = (HDC) wParam;
// 得到視窗的用戶端區域
CRect rc;
GetClientRect (rc);
// 得到位元影像DC
CDC dcImage;
dcImage.CreateCompatibleDC (hDC);
HBITMAP hOldBmp = dcImage.SelectBitmap(*m_bmBitmap);
// 得到位元影像寬和高
BITMAP bm;
m_bmBitmap->GetBitmap (&bm);
// 使用最小寬和高
int width = min (bm.bmWidth, rc.Width());
int height = min (bm.bmHeight, rc.Height());
// 繪製位元影像為視窗背景
BitBlt (hDC, 0, 0, rc.Width(), rc.Height(), dcImage, 0, 0, SRCCOPY);
dcImage.SelectBitmap (hOldBmp);
dcImage.DeleteDC ();
return TRUE;
}
4. 處理左鍵按下(用於拖動視窗)
MESSAGE_HANDLER(WM_LBUTTONDOWN, OnLButtonDown)
LRESULT CMainDlg::OnLButtonDown(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
CPoint pt(lParam);
// 欺騙視窗,讓它以為滑鼠點擊的是標題列
PostMessage (WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(pt.x,pt.y));
return 0;
}
效果如所示:
註:由於我隨便選的一張圖片,其周圍背景不是純粹的全部黑色,所以邊緣會有毛刺,用Photoshop扣一下邊就能解決這個問題了。