创建不规则窗口的三种方法:通过区域相关API设置窗口的区域SetWindowRgn;通过SetLayeredWindowAttributes来指定特殊的透明颜色,让背景图的部分位置全透从而实现窗口的“不规则”;通过UpdateLayeredWindow来指定特殊颜色透明或者根据图片的ALPHA值来设置窗口全透。
方法一是是真正的异形不规则窗口,而方法二、三则是通过部分区域全透来实现异形。这里只讨论后面两种方法,使用广泛也易于实现。
不管是SetLayeredWindowAttributes还是UpdateLayeredWindow在调用前都需要为窗口加上WS_EX_LAYERED扩展属性,否则API调用失败,详见MSDN;
<span style="color:#999999;"> DWORD dwExStyle=GetWindowLong(m_hWnd, GWL_EXSTYLE); ::SetWindowLong(m_hWnd, GWL_EXSTYLE, dwExStyle|WS_EX_LAYERED);</span>
首先需要一张图,需要全透的地方我们用同一种颜色来填充:
需要透明的区域我用的纯白色0XFFFFFF来填充的,那么我们就需要指定SetLayeredWindowAttributes的第二个参数为0XFFFFFF,第三个参数LWA_COLORKEY,即使颜色值为纯白的像素点全透。
接下来要做的就是双缓冲绘图了,这个很基础的了,不细说上代码:
<span style="color:#999999;">case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... HDC hMemDC=CreateCompatibleDC(hdc); HBITMAP hBkBmp=LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1)); HBITMAP hOldBmp=(HBITMAP)SelectObject(hMemDC, hBkBmp); BitBlt(hdc, 0, 0, 224, 114, hMemDC, 0, 0, SRCCOPY); SelectObject(hMemDC, hOldBmp); DeleteDC(hMemDC); DeleteObject(hBkBmp); EndPaint(hWnd, &ps); break; }</span>
完整源码下载地址:点击下载代码
2、使用UpdateLayeredWindow来设置不规则窗口,和SetLayeredWindowAttributes一样,既可以指定透明颜色值,也可以根据图片像素点的实际ALPHA值来设置透明度。指定透明颜色值的话和上面的例子差不多,不再啰嗦。我们可以手动来设置某些像素的ALPHA值为0,使其全透。
CreateDIBSection将位图像素值加载到内存中,通过循环来设置我们需要透明的区域的ALPHA值为0,从而实现不规则窗口:
case WM_PAINT: { hdc = BeginPaint(hWnd, &ps); // TODO: 在此添加任意绘图代码... RECT rcClient; GetClientRect(g_hWnd, &rcClient); int nWidth=rcClient.right-rcClient.left; int nHeight=rcClient.bottom-rcClient.top; if ( NULL == g_hMemDC ) { g_hMemDC=CreateCompatibleDC(hdc); //g_hBitmap=LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1)); g_hBitmap=CreateCompatibleBitmap(hdc, nWidth, nHeight); SelectObject(g_hMemDC, g_hBitmap); BITMAPINFO bi; ZeroMemory(&bi, sizeof(BITMAPINFO)); bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth=nWidth; bi.bmiHeader.biHeight=nHeight; bi.bmiHeader.biPlanes=1; bi.bmiHeader.biBitCount=32; bi.bmiHeader.biCompression=BI_RGB; bi.bmiHeader.biSizeImage=nWidth*nHeight*4; BYTE* lpData=NULL; HRGN hRgn=CreateRoundRectRgn(0, 0, 100, 100, 100, 100); g_hBitmap=CreateDIBSection(g_hMemDC, &bi, DIB_RGB_COLORS, (void**)&lpData, NULL, 0); for (int i=0; i<nHeight; ++i) for (int j=0; j<nWidth; ++j) { int nPos=( (nHeight-i-1)*nWidth+j)*4; //lpData[nPos+0]=0xff; //lpData[nPos+3]=0x00; if ( !PtInRegion(hRgn, j, i) ) lpData[nPos+3]=0x0f; else { lpData[nPos+0]=0xff; lpData[nPos+3]=0x00; } } DeleteObject(hRgn); SelectObject(g_hMemDC, g_hBitmap); } POINT pt={300, 300}; POINT pt1={0, 0}; SIZE size={300, 300}; BLENDFUNCTION bf; ZeroMemory(&bf, sizeof(bf)); bf.SourceConstantAlpha=255; bf.AlphaFormat=AC_SRC_ALPHA; LONG lStyle=GetWindowLong(hWnd, GWL_EXSTYLE); SetWindowLong(hWnd, GWL_EXSTYLE, lStyle|WS_EX_LAYERED); UpdateLayeredWindow(hWnd, hdc, &pt, &size, g_hMemDC, &pt1, 0, &bf, ULW_ALPHA); EndPaint(hWnd, &ps); break; }
完整源码下载地址:点击下载源码
另外,切记SetLayeredWindowAttributes和UpdateLayeredWindow 混合使用的时候:
Note that once SetLayeredWindowAttributes has been called for a layered window, subsequent UpdateLayeredWindow calls will fail until the layering style bit is cleared and set again.(MSDN)
使用了SetLayeredWindowAttributes 后调用UpdateLayeredWindow 需要重新设置
<span style="color:#999999;">WS_EX_LAYERED</span>
<span style="color:#999999;"> </span>
属性,否则API调用失败。