在使用duilib过程中,发现很多人提到关于创建窗体边框阴影效果的问题,查阅了下相关资料,找到如下代码,记录如下:
WndShadow.h头文件
1 | #pragma once |
2 |
3 | #include "map" |
4 |
5 | class CWndShadow |
6 | { |
7 | public : |
8 | CWndShadow( void ); |
9 | public : |
10 | virtual ~CWndShadow( void ); |
11 |
12 | protected : |
13 |
14 | // Instance handle, used to register window class and create window |
15 | static HINSTANCE s_hInstance; |
16 |
17 | // Parent HWND and CWndShadow object pares, in order to find CWndShadow in ParentProc() |
18 | static std::map< HWND , CWndShadow *> s_Shadowmap; |
19 |
20 | // |
21 | typedef BOOL (WINAPI *pfnUpdateLayeredWindow)( HWND hWnd, HDC hdcDst, POINT *pptDst, |
22 | SIZE *psize, HDC hdcSrc, POINT *pptSrc, COLORREF crKey, |
23 | BLENDFUNCTION *pblend, DWORD dwFlags); |
24 | static pfnUpdateLayeredWindow s_UpdateLayeredWindow; |
25 |
26 | HWND m_hWnd; |
27 |
28 | LONG m_OriParentProc; // Original WndProc of parent window |
29 |
30 | enum ShadowStatus |
31 | { |
32 | SS_ENABLED = 1, // Shadow is enabled, if not, the following one is always false |
33 | SS_VISABLE = 1 << 1, // Shadow window is visible |
34 | SS_PARENTVISIBLE = 1<< 2 // Parent window is visible, if not, the above one is always false |
35 | }; |
36 | BYTE m_Status; |
37 |
38 | unsigned char m_nDarkness; // Darkness, transparency of blurred area |
39 | unsigned char m_nSharpness; // Sharpness, width of blurred border of shadow window |
40 | signed char m_nSize; // Shadow window size, relative to parent window size |
41 |
42 | // The X and Y offsets of shadow window, |
43 | // relative to the parent window, at center of both windows (not top-left corner), signed |
44 | signed char m_nxOffset; |
45 | signed char m_nyOffset; |
46 |
47 | // Restore last parent window size, used to determine the update strategy when parent window is resized |
48 | LPARAM m_WndSize; |
49 |
50 | // Set this to true if the shadow should not be update until next WM_PAINT is received |
51 | bool m_bUpdate; |
52 |
53 | COLORREF m_Color; // Color of shadow |
54 |
55 | public : |
56 | static bool Initialize( HINSTANCE hInstance); |
57 | void Create( HWND hParentWnd); |
58 |
59 | bool SetSize( int NewSize = 0); |
60 | bool SetSharpness(unsigned int NewSharpness = 5); |
61 | bool SetDarkness(unsigned int NewDarkness = 200); |
62 | bool SetPosition( int NewXOffset = 5, int NewYOffset = 5); |
63 | bool SetColor( COLORREF NewColor = 0); |
64 |
65 | protected : |
66 | //static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); |
67 | static LRESULT CALLBACK ParentProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); |
68 |
69 | // Redraw, resize and move the shadow |
70 | // called when window resized or shadow properties changed, but not only moved without resizing |
71 | void Update( HWND hParent); |
72 |
73 | // Fill in the shadow window alpha blend bitmap with shadow image pixels |
74 | void MakeShadow( UINT32 *pShadBits, HWND hParent, RECT *rcParent); |
75 |
76 | // Helper to calculate the alpha-premultiled value for a pixel |
77 | inline DWORD PreMultiply( COLORREF cl, unsigned char nAlpha) |
78 | { |
79 | // It's strange that the byte order of RGB in 32b BMP is reverse to in COLORREF |
80 | return (GetRValue(cl) * ( DWORD )nAlpha / 255) << 16 | |
81 | (GetGValue(cl) * ( DWORD )nAlpha / 255) << 8 | |
82 | (GetBValue(cl) * ( DWORD )nAlpha / 255); |
83 | } |
84 | }; |
WndShadow.cpp源文件
1 | #include "StdAfx.h" |
2 | #include "WndShadow.h" |
3 | #include "math.h" |
4 | #include "crtdbg.h" |
5 |
6 | // Some extra work to make this work in VC++ 6.0 |
7 |
8 | // walk around the for iterator scope bug of VC++6.0 |
9 | #ifdef _MSC_VER |
10 | #if _MSC_VER == 1200 |
11 | #define for if(false);else for |
12 | #endif |
13 | #endif |
14 |
15 | // Some definitions for VC++ 6.0 without newest SDK |
16 | #ifndef WS_EX_LAYERED |
17 | #define WS_EX_LAYERED 0x00080000 |
18 | #endif |
19 |
20 | #ifndef AC_SRC_ALPHA |
21 | #define AC_SRC_ALPHA 0x01 |
22 | #endif |
23 |
24 | #ifndef ULW_ALPHA |
25 | #define ULW_ALPHA 0x00000002 |
26 | #endif |
27 |
28 | CWndShadow::pfnUpdateLayeredWindow CWndShadow::s_UpdateLayeredWindow = NULL; |
29 |
30 | const TCHAR *strWndClassName = _T( "PerryShadowWnd" ); |
31 |
32 | HINSTANCE CWndShadow::s_hInstance = ( HINSTANCE )INVALID_HANDLE_VALUE; |
33 |
34 | std::map< HWND , CWndShadow *> CWndShadow::s_Shadowmap; |
35 |
36 | CWndShadow::CWndShadow( void ) |
37 | : m_hWnd(( HWND )INVALID_HANDLE_VALUE) |
38 | , m_OriParentProc(NULL) |
39 | , m_nDarkness(150) |
40 | , m_nSharpness(5) |
41 | , m_nSize(0) |
42 | , m_nxOffset(5) |
43 | , m_nyOffset(5) |
44 | , m_Color(RGB(0, 0, 0)) |
45 | , m_WndSize(0) |
46 | , m_bUpdate( false ) |
47 | { |
48 | } |
49 |
50 | CWndShadow::~CWndShadow( void ) |
51 | { |
52 | } |
53 |
54 | bool CWndShadow::Initialize( HINSTANCE hInstance) |
55 | { |
56 | // Should not initiate more than once |
57 | if (NULL != s_UpdateLayeredWindow) |
58 | return false ; |
59 |
60 | HMODULE hUser32 = GetModuleHandle(_T( "USER32.DLL" )); |
61 | s_UpdateLayeredWindow = |
62 | (pfnUpdateLayeredWindow)GetProcAddress(hUser32, |
63 | "UpdateLayeredWindow" ); |
64 |
65 | // If the import did not succeed, make sure your app can handle it! |
66 | if (NULL == s_UpdateLayeredWindow) |
67 | return false ; |
68 |
69 | // Store the instance handle |
70 | s_hInstance = hInstance; |
71 |
72 | // Register window class for shadow window |
73 | WNDCLASSEX wcex; |
74 |
75 | memset (&wcex, 0, sizeof (wcex)); |
76 |
77 | wcex.cbSize = sizeof (WNDCLASSEX); |
78 | wcex.style = CS_HREDRAW | CS_VREDRAW; |
79 | wcex.lpfnWndProc = DefWindowProc; |
80 | wcex.cbClsExtra = 0; |
81 | wcex.cbWndExtra = 0; |
82 | wcex.hInstance = hInstance; |
83 | wcex.hIcon = NULL; |
84 | wcex.hCursor = LoadCursor(NULL, IDC_ARROW); |
85 | wcex.hbrBackground = ( HBRUSH )(COLOR_WINDOW+1); |
86 | wcex.lpszMenuName = NULL; |
87 | wcex.lpszClassName = strWndClassName; |
88 | wcex.hIconSm = NULL; |
89 |
90 | RegisterClassEx(&wcex); |
91 |
92 | return true ; |
93 | } |
94 |
95 | void CWndShadow::Create( HWND hParentWnd) |
96 | { |
97 | // Do nothing if the system does not support layered windows |
98 | if (NULL == s_UpdateLayeredWindow) |
99 | return ; |
100 |
101 | // Already initialized |
102 | _ASSERT(s_hInstance != INVALID_HANDLE_VALUE); |
103 |
104 | // Add parent window - shadow pair to the map |
105 | _ASSERT(s_Shadowmap.find(hParentWnd) == s_Shadowmap.end()); // Only one shadow for each window |
106 | s_Shadowmap[hParentWnd] = this ; |
107 |
108 | // Create the shadow window |
109 | m_hWnd = CreateWindowEx(WS_EX_LAYERED | WS_EX_TRANSPARENT, strWndClassName, NULL, |
110 | /*WS_VISIBLE | */ WS_CAPTION | WS_POPUPWINDOW, |
111 | CW_USEDEFAULT, 0, 0, 0, hParentWnd, NULL, s_hInstance, NULL); |
112 |
113 | // Determine the initial show state of shadow according to parent window's state |
114 | LONG lParentStyle = GetWindowLong(hParentWnd, GWL_STYLE); |
115 | if (!(WS_VISIBLE & lParentStyle)) // Parent invisible |
116 | m_Status = SS_ENABLED; |
117 | else if ((WS_MAXIMIZE | WS_MINIMIZE) & lParentStyle) // Parent visible but does not need shadow |
118 | m_Status = SS_ENABLED | SS_PARENTVISIBLE; |
119 | else // Show the shadow |
120 | { |
121 | m_Status = SS_ENABLED | SS_VISABLE | SS_PARENTVISIBLE; |
122 | ::ShowWindow(m_hWnd, SW_SHOWNA); |
123 | Update(hParentWnd); |
124 | } |
125 |
126 | // Replace the original WndProc of parent window to steal messages |
127 | m_OriParentProc = GetWindowLong(hParentWnd, GWL_WNDPROC); |
128 |
129 | #pragma warning(disable: 4311) // temporrarily disable the type_cast warning in Win32 |
130 | SetWindowLong(hParentWnd, GWL_WNDPROC, ( LONG )ParentProc); |
131 | #pragma warning(default: 4311) |
132 |
133 | } |
134 |
135 | LRESULT CALLBACK CWndShadow::ParentProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) |
136 | { |
137 | _ASSERT(s_Shadowmap.find(hwnd) != s_Shadowmap.end()); // Shadow must have been attached |
138 |
139 | CWndShadow *pThis = s_Shadowmap[hwnd]; |
140 |
141 | switch (uMsg) |
142 | { |
143 | case WM_MOVE: |
144 | if (pThis->m_Status & SS_VISABLE) |
145 | { |
146 | RECT WndRect; |
147 | GetWindowRect(hwnd, &WndRect); |
148 | SetWindowPos(pThis->m_hWnd, 0, |
149 | WndRect.left + pThis->m_nxOffset - pThis->m_nSize, WndRect.top + pThis->m_nyOffset - pThis->m_nSize, |
150 | 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); |
151 | } |
152 | break ; |
153 |
154 | case WM_SIZE: |
155 | if (pThis->m_Status & SS_ENABLED) |
156 | { |
157 | if (SIZE_MAXIMIZED == wParam || SIZE_MINIMIZED == wParam) |
158 | { |
159 | ::ShowWindow(pThis->m_hWnd, SW_HIDE); |
160 | pThis->m_Status &= ~SS_VISABLE; |
161 | } |
162 | else if (pThis->m_Status & SS_PARENTVISIBLE) // Parent maybe resized even if invisible |
163 | { |
164 | // Awful! It seems that if the window size was not decreased |
165 | // the window region would never be updated until WM_PAINT was sent. |
166 | // So do not Update() until next WM_PAINT is received in this case |
167 | if (LOWORD(lParam) > LOWORD(pThis->m_WndSize) || HIWORD(lParam) > HIWORD(pThis->m_WndSize)) |
168 | pThis->m_bUpdate = true ; |
169 | else |
170 | pThis->Update(hwnd); |
171 | if (!(pThis->m_Status & SS_VISABLE)) |
172 | { |
173 | ::ShowWindow(pThis->m_hWnd, SW_SHOWNA); |
174 | pThis->m_Status |= SS_VISABLE; |
175 | } |
176 | } |
177 | pThis->m_WndSize = lParam; |
178 | } |
179 | break ; |
180 |
181 | case WM_PAINT: |
182 | { |
183 | if (pThis->m_bUpdate) |
184 | { |
185 | pThis->Update(hwnd); |
186 | pThis->m_bUpdate = false ; |
187 | } |
188 | //return hr; |
189 | break ; |
190 | } |
191 |
192 | // In some cases of sizing, the up-right corner of the parent window region would not be properly updated |
193 | // Update() again when sizing is finished |
194 | case WM_EXITSIZEMOVE: |
195 | if (pThis->m_Status & SS_VISABLE) |
196 | { |
197 | pThis->Update(hwnd); |
198 | } |
199 | break ; |
200 |
201 | case WM_SHOWWINDOW: |
202 | if (pThis->m_Status & SS_ENABLED) |
203 | { |
204 | if (!wParam) // the window is being hidden |
205 | { |
206 | ::ShowWindow(pThis->m_hWnd, SW_HIDE); |
207 | pThis->m_Status &= ~(SS_VISABLE | SS_PARENTVISIBLE); |
208 | } |
209 | else if (!(pThis->m_Status & SS_PARENTVISIBLE)) |
210 | { |
211 | //pThis->Update(hwnd); |
212 | pThis->m_bUpdate = true ; |
213 | ::ShowWindow(pThis->m_hWnd, SW_SHOWNA); |
214 | pThis->m_Status |= SS_VISABLE | SS_PARENTVISIBLE; |
215 | } |
216 | } |
217 | break ; |
218 |
219 | case WM_DESTROY: |
220 | DestroyWindow(pThis->m_hWnd); // Destroy the shadow |
221 | break ; |
222 |
223 | case WM_NCDESTROY: |
224 | s_Shadowmap.erase(hwnd); // Remove this window and shadow from the map |
225 | break ; |
226 |
227 | } |
228 |
229 | #pragma warning(disable: 4312) // temporrarily disable the type_cast warning in Win32 |
230 | // Call the default(original) window procedure for other messages or messages processed but not returned |
231 | return ((WNDPROC)pThis->m_OriParentProc)(hwnd, uMsg, wParam, lParam); |
232 | #pragma warning(default: 4312) |
233 |
234 | } |
235 |
236 | void CWndShadow::Update( HWND hParent) |
237 | { |
238 | //int ShadSize = 5; |
239 | //int Multi = 100 / ShadSize; |
240 |
241 | RECT WndRect; |
242 | GetWindowRect(hParent, &WndRect); |
243 | int nShadWndWid = WndRect.right - WndRect.left + m_nSize * 2; |
244 | int nShadWndHei = WndRect.bottom - WndRect.top + m_nSize * 2; |
245 |
246 | // Create the alpha blending bitmap |
247 | BITMAPINFO bmi; // bitmap header |
248 |
249 | ZeroMemory(&bmi, sizeof (BITMAPINFO)); |
250 | bmi.bmiHeader.biSize = sizeof (BITMAPINFOHEADER); |
251 | bmi.bmiHeader.biWidth = nShadWndWid; |
252 | bmi.bmiHeader.biHeight = nShadWndHei; |
253 | bmi.bmiHeader.biPlanes = 1; |
254 | bmi.bmiHeader.biBitCount = 32; // four 8-bit components |
255 | bmi.bmiHeader.biCompression = BI_RGB; |
256 | bmi.bmiHeader.biSizeImage = nShadWndWid * nShadWndHei * 4; |
257 |
258 | BYTE *pvBits; // pointer to DIB section |
259 | HBITMAP hbitmap = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, ( void **)&pvBits, NULL, 0); |
260 |
261 | ZeroMemory(pvBits, bmi.bmiHeader.biSizeImage); |
262 | MakeShadow(( UINT32 *)pvBits, hParent, &WndRect); |
263 |
264 | HDC hMemDC = CreateCompatibleDC(NULL); |
265 | HBITMAP hOriBmp = ( HBITMAP )SelectObject(hMemDC, hbitmap); |
266 |
267 | POINT ptDst = {WndRect.left + m_nxOffset - m_nSize, WndRect.top + m_nyOffset - m_nSize}; |
268 | POINT ptSrc = {0, 0}; |
269 | SIZE WndSize = {nShadWndWid, nShadWndHei}; |
270 | BLENDFUNCTION blendPixelFunction= { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; |
271 |
272 | MoveWindow(m_hWnd, ptDst.x, ptDst.y, nShadWndWid, nShadWndHei, FALSE); |
273 |
274 | BOOL bRet= s_UpdateLayeredWindow(m_hWnd, NULL, &ptDst, &WndSize, hMemDC, |
275 | &ptSrc, 0, &blendPixelFunction, ULW_ALPHA); |
276 |
277 | _ASSERT(bRet); // something was wrong.... |
278 |
279 | // Delete used resources |
280 | SelectObject(hMemDC, hOriBmp); |
281 | DeleteObject(hbitmap); |
282 | DeleteDC(hMemDC); |
283 |
284 | } |
285 |
286 | void CWndShadow::MakeShadow( UINT32 *pShadBits, HWND hParent, RECT *rcParent) |
287 | { |
288 | // The shadow algorithm: |
289 | // Get the region of parent window, |
290 | // Apply morphologic erosion to shrink it into the size (ShadowWndSize - Sharpness) |
291 | // Apply modified (with blur effect) morphologic dilation to make the blurred border |
292 | // The algorithm is optimized by assuming parent window is just "one piece" and without "wholes" on it |
293 |
294 | // Get the region of parent window, |
295 | HRGN hParentRgn = CreateRectRgn(0, 0, 0, 0); |
296 | GetWindowRgn(hParent, hParentRgn); |
297 |
298 | // Determine the Start and end point of each horizontal scan line |
299 | SIZE szParent = {rcParent->right - rcParent->left, rcParent->bottom - rcParent->top}; |
300 | SIZE szShadow = {szParent.cx + 2 * m_nSize, szParent.cy + 2 * m_nSize}; |
301 | // Extra 2 lines (set to be empty) in ptAnchors are used in dilation |
302 | int nAnchors = max(szParent.cy, szShadow.cy); // # of anchor points pares |
303 | int (*ptAnchors)[2] = new int [nAnchors + 2][2]; |
304 | int (*ptAnchorsOri)[2] = new int [szParent.cy][2]; // anchor points, will not modify during erosion |
305 | ptAnchors[0][0] = szParent.cx; |
306 | ptAnchors[0][1] = 0; |
307 | ptAnchors[nAnchors + 1][0] = szParent.cx; |
308 | ptAnchors[nAnchors + 1][1] = 0; |
309 | if (m_nSize > 0) |
310 | { |
311 | // Put the parent window anchors at the center |
312 | for ( int i = 0; i < m_nSize; i++) |
313 | { |
314 | ptAnchors[i + 1][0] = szParent.cx; |
315 | ptAnchors[i + 1][1] = 0; |
316 | ptAnchors[szShadow.cy - i][0] = szParent.cx; |
317 | ptAnchors[szShadow.cy - i][1] = 0; |
318 | } |
319 | ptAnchors += m_nSize; |
320 | } |
321 | for ( int i = 0; i < szParent.cy; i++) |
322 | { |
323 | // find start point |
324 | int j; |
325 | for (j = 0; j < szParent.cx; j++) |
326 | { |
327 | if (PtInRegion(hParentRgn, j, i)) |
328 | { |
329 | ptAnchors[i + 1][0] = j + m_nSize; |
330 | ptAnchorsOri[i][0] = j; |
331 | break ; |
332 | } |
333 | } |
334 |
335 | if (j >= szParent.cx) // Start point not found |
336 | { |
337 | ptAnchors[i + 1][0] = szParent.cx; |
338 | ptAnchorsOri[i][1] = 0; |
339 | ptAnchors[i + 1][0] = szParent.cx; |
340 | ptAnchorsOri[i][1] = 0; |
341 | } |
342 | else |
343 | { |
344 | // find end point |
345 | for (j = szParent.cx - 1; j >= ptAnchors[i + 1][0]; j--) |
346 | { |
347 | if (PtInRegion(hParentRgn, j, i)) |
348 | { |
349 | ptAnchors[i + 1][1] = j + 1 + m_nSize; |
350 | ptAnchorsOri[i][1] = j + 1; |
351 | break ; |
352 | } |
353 | } |
354 | } |
355 | } |
356 |
357 | if (m_nSize > 0) |
358 | ptAnchors -= m_nSize; // Restore pos of ptAnchors for erosion |
359 | int (*ptAnchorsTmp)[2] = new int [nAnchors + 2][2]; // Store the result of erosion |
360 | // First and last line should be empty |
361 | ptAnchorsTmp[0][0] = szParent.cx; |
362 | ptAnchorsTmp[0][1] = 0; |
363 | ptAnchorsTmp[nAnchors + 1][0] = szParent.cx; |
364 | ptAnchorsTmp[nAnchors + 1][1] = 0; |
365 | int nEroTimes = 0; |
366 | // morphologic erosion |
367 | for ( int i = 0; i < m_nSharpness - m_nSize; i++) |
368 | { |
369 | nEroTimes++; |
370 | //ptAnchorsTmp[1][0] = szParent.cx; |
371 | //ptAnchorsTmp[1][1] = 0; |
372 | //ptAnchorsTmp[szParent.cy + 1][0] = szParent.cx; |
373 | //ptAnchorsTmp[szParent.cy + 1][1] = 0; |
374 | for ( int j = 1; j < nAnchors + 1; j++) |
375 | { |
376 | ptAnchorsTmp[j][0] = max(ptAnchors[j - 1][0], max(ptAnchors[j][0], ptAnchors[j + 1][0])) + 1; |
377 | ptAnchorsTmp[j][1] = min(ptAnchors[j - 1][1], min(ptAnchors[j][1], ptAnchors[j + 1][1])) - 1; |
378 | } |
379 | // Exchange ptAnchors and ptAnchorsTmp; |
380 | int (*ptAnchorsXange)[2] = ptAnchorsTmp; |
381 | ptAnchorsTmp = ptAnchors; |
382 | ptAnchors = ptAnchorsXange; |
383 | } |
384 |
385 | // morphologic dilation |
386 | ptAnchors += (m_nSize < 0 ? -m_nSize : 0) + 1; // now coordinates in ptAnchors are same as in shadow window |
387 | // Generate the kernel |
388 | int nKernelSize = m_nSize > m_nSharpness ? m_nSize : m_nSharpness; |
389 | int nCenterSize = m_nSize > m_nSharpness ? (m_nSize - m_nSharpness) : 0; |
390 | UINT32 *pKernel = new UINT32 [(2 * nKernelSize + 1) * (2 * nKernelSize + 1)]; |
391 | UINT32 *pKernelIter = pKernel; |
392 | for ( int i = 0; i <= 2 * nKernelSize; i++) |
393 | { |
394 | for ( int j = 0; j <= 2 * nKernelSize; j++) |
395 | { |
396 | double dLength = sqrt ((i - nKernelSize) * (i - nKernelSize) + (j - nKernelSize) * ( double )(j - nKernelSize)); |
397 | if (dLength < nCenterSize) |
398 | *pKernelIter = m_nDarkness << 24 | PreMultiply(m_Color, m_nDarkness); |
399 | else if (dLength <= nKernelSize) |
400 | { |
401 | UINT32 nFactor = (( UINT32 )((1 - (dLength - nCenterSize) / (m_nSharpness + 1)) * m_nDarkness)); |
402 | *pKernelIter = nFactor << 24 | PreMultiply(m_Color, nFactor); |
403 | } |
404 | else |
405 | *pKernelIter = 0; |
406 | //TRACE("%d ", *pKernelIter >> 24); |
407 | pKernelIter ++; |
408 | } |
409 | //TRACE("\n"); |
410 | } |
411 | // Generate blurred border |
412 | for ( int i = nKernelSize; i < szShadow.cy - nKernelSize; i++) |
413 | { |
414 | int j; |
415 | if (ptAnchors[i][0] < ptAnchors[i][1]) |
416 | { |
417 |
418 | // Start of line |
419 | for (j = ptAnchors[i][0]; |
420 | j < min(max(ptAnchors[i - 1][0], ptAnchors[i + 1][0]) + 1, ptAnchors[i][1]); |
421 | j++) |
422 | { |
423 | for ( int k = 0; k <= 2 * nKernelSize; k++) |
424 | { |
425 | UINT32 *pPixel = pShadBits + |
426 | (szShadow.cy - i - 1 + nKernelSize - k) * szShadow.cx + j - nKernelSize; |
427 | UINT32 *pKernelPixel = pKernel + k * (2 * nKernelSize + 1); |
428 | for ( int l = 0; l <= 2 * nKernelSize; l++) |
429 | { |
430 | if (*pPixel < *pKernelPixel) |
431 | *pPixel = *pKernelPixel; |
432 | pPixel++; |
433 | pKernelPixel++; |
434 | } |
435 | } |
436 | } // for() start of line |
437 |
438 | // End of line |
439 | for (j = max(j, min(ptAnchors[i - 1][1], ptAnchors[i + 1][1]) - 1); |
440 | j < ptAnchors[i][1]; |
441 | j++) |
442 | { |
443 | for ( int k = 0; k <= 2 * nKernelSize; k++) |
444 | { |
445 | UINT32 *pPixel = pShadBits + |
446 | (szShadow.cy - i - 1 + nKernelSize - k) * szShadow.cx + j - nKernelSize; |
447 | UINT32 *pKernelPixel = pKernel + k * (2 * nKernelSize + 1); |
448 | for ( int l = 0; l <= 2 * nKernelSize; l++) |
449 | { |
450 | if (*pPixel < *pKernelPixel) |
451 | *pPixel = *pKernelPixel; |
452 | pPixel++; |
453 | pKernelPixel++; |
454 | } |
455 | } |
456 | } // for() end of line |
457 |
458 | } |
459 | } // for() Generate blurred border |
460 |
461 | // Erase unwanted parts and complement missing |
462 | UINT32 clCenter = m_nDarkness << 24 | PreMultiply(m_Color, m_nDarkness); |
463 | for ( int i = min(nKernelSize, max(m_nSize - m_nyOffset, 0)); |
464 | i < max(szShadow.cy - nKernelSize, min(szParent.cy + m_nSize - m_nyOffset, szParent.cy + 2 * m_nSize)); |
465 | i++) |
466 | { |
467 | UINT32 *pLine = pShadBits + (szShadow.cy - i - 1) * szShadow.cx; |
468 | if (i - m_nSize + m_nyOffset < 0 || i - m_nSize + m_nyOffset >= szParent.cy) // Line is not covered by parent window |
469 | { |
470 | for ( int j = ptAnchors[i][0]; j < ptAnchors[i][1]; j++) |
471 | { |
472 | *(pLine + j) = clCenter; |
473 | } |
474 | } |
475 | else |
476 | { |
477 | for ( int j = ptAnchors[i][0]; |
478 | j < min(ptAnchorsOri[i - m_nSize + m_nyOffset][0] + m_nSize - m_nxOffset, ptAnchors[i][1]); |
479 | j++) |
480 | *(pLine + j) = clCenter; |
481 | for ( int j = max(ptAnchorsOri[i - m_nSize + m_nyOffset][0] + m_nSize - m_nxOffset, 0); |
482 | j < min(ptAnchorsOri[i - m_nSize + m_nyOffset][1] + m_nSize - m_nxOffset, szShadow.cx); |
483 | j++) |
484 | *(pLine + j) = 0; |
485 | for ( int j = max(ptAnchorsOri[i - m_nSize + m_nyOffset][1] + m_nSize - m_nxOffset, ptAnchors[i][0]); |
486 | j < ptAnchors[i][1]; |
487 | j++) |
488 | *(pLine + j) = clCenter; |
489 | } |
490 | } |
491 |
492 | // Delete used resources |
493 | delete [] (ptAnchors - (m_nSize < 0 ? -m_nSize : 0) - 1); |
494 | delete [] ptAnchorsTmp; |
495 | delete [] ptAnchorsOri; |
496 | delete [] pKernel; |
497 | DeleteObject(hParentRgn); |
498 | } |
499 |
500 | bool CWndShadow::SetSize( int NewSize) |
501 | { |
502 | if (NewSize > 20 || NewSize < -20) |
503 | return false ; |
504 |
505 | m_nSize = ( signed char )NewSize; |
506 | if (SS_VISABLE & m_Status) |
507 | Update(GetParent(m_hWnd)); |
508 | return true ; |
509 | } |
510 |
511 | bool CWndShadow::SetSharpness(unsigned int NewSharpness) |
512 | { |
513 | if (NewSharpness > 20) |
514 | return false ; |
515 |
516 | m_nSharpness = (unsigned char )NewSharpness; |
517 | if (SS_VISABLE & m_Status) |
518 | Update(GetParent(m_hWnd)); |
519 | return true ; |
520 | } |
521 |
522 | bool CWndShadow::SetDarkness(unsigned int NewDarkness) |
523 | { |
524 | if (NewDarkness > 255) |
525 | return false ; |
526 |
527 | m_nDarkness = (unsigned char )NewDarkness; |
528 | if (SS_VISABLE & m_Status) |
529 | Update(GetParent(m_hWnd)); |
530 | return true ; |
531 | } |
532 |
533 | bool CWndShadow::SetPosition( int NewXOffset, int NewYOffset) |
534 | { |
535 | if (NewXOffset > 20 || NewXOffset < -20 || |
536 | NewYOffset > 20 || NewYOffset < -20) |
537 | return false ; |
538 |
539 | m_nxOffset = ( signed char )NewXOffset; |
540 | m_nyOffset = ( signed char )NewYOffset; |
541 | if (SS_VISABLE & m_Status) |
542 | Update(GetParent(m_hWnd)); |
543 | return true ; |
544 | } |
545 |
546 | bool CWndShadow::SetColor( COLORREF NewColor) |
547 | { |
548 | m_Color = NewColor; |
549 | if (SS_VISABLE & m_Status) |
550 | Update(GetParent(m_hWnd)); |
551 | return true ; |
552 | } |
Duilib中使用方法:
1. 在要添加阴影效果的窗体类中包含WndShadow的头文件,并创建对应类型的成员变量m_WndShadow。
2. 在WinMain处添加初始化
1 | // Initiation of the shadow |
2 | CWndShadow::Initialize(hInstance); |
3. 窗体OnCreate中设置
1 | LRESULT OnCreate( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL & bHandled) |
2 | { |
3 | LONG styleValue = ::GetWindowLong(* this , GWL_STYLE); |
4 | styleValue &= ~WS_CAPTION; |
5 | ::SetWindowLong(* this , GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN); |
6 | m_WndShadow.Create(m_hWnd); |
7 | m_WndShadow.SetSize(4); |
8 | m_WndShadow.SetPosition(0, 0); |
9 |
10 | //blablabla.... |
11 | return 0; |
12 | } |
实例效果(此处以Duilib中的360Demo为例):
1. 未添加窗体边框阴影效果图(点击图片可查看大图)
2. 添加窗体边框阴影效果图(点击图片可查看大图)
说明:此处为了更清除的看到窗体边框阴影效果,我将调用的地方原来的m_WndShadow.SetSize(4);改为
了m_WndShadow.SetSize(15);正常情况下,Size设置为4的效果即可。
【WndShadow文件下载:http://yun.baidu.com/s/1mZjmY】
联系客服