不规则按钮,支持普通Button,Radio Button, Check Button

使用白色作为透明色Mask创建不规则region

发几张图片(程序以bmp做例子,但是论坛不能传bmp)上来做个具体例子:

normal

down

focus

diable

mask

 1 //Header File
 2 
 3 #pragma once
 4 
 5 
 6 // CRgnButton
 7 
 8 #define WM_CXSHADE_RADIO    WM_USER+0x100
 9 #define ALLOC_UNIT  100
10 
11 class CRgnButton : public CButton
12 {
13     DECLARE_DYNAMIC(CRgnButton)
14 
15 public:
16     CRgnButton();
17     virtual ~CRgnButton();
18 
19     enum DRAW_MODE { DRAW_NORMAL, DRAW_STRETCH, DRAW_TILED };
20 public:
21     void SetToolTipText(const CString &strTip);
22     COLORREF SetTextColor(COLORREF colorNew);
23     void SetSkin(UINT normal, UINT down, UINT over=0, UINT disabled=0, UINT focus=0,UINT mask=0,
24         DRAW_MODE drawmode=DRAW_NORMAL,short border=0,short margin=0);
25 public:
26     virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/);
27 protected:
28     virtual void PreSubclassWindow();
29 
30     afx_msg BOOL OnEraseBkgnd(CDC* pDC);
31     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);
32     afx_msg void OnLButtonUp(UINT nFlags, CPoint point);
33     afx_msg void OnMouseMove(UINT nFlags, CPoint point);
34     afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point);
35     afx_msg void OnKillFocus(CWnd* pNewWnd);
36     afx_msg BOOL OnBnClicked();
37     afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
38 
39     afx_msg HRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam);
40     afx_msg HRESULT OnRadioInfo(WPARAM wParam, LPARAM lParam);
41     afx_msg HRESULT OnBMSetCheck(WPARAM wParam, LPARAM lParam);
42     afx_msg HRESULT OnBMGetCheck(WPARAM wParam, LPARAM lParam);
43     DECLARE_MESSAGE_MAP()
44 
45 protected:
46     HRGN    CreateRgnFromBitmap(CBitmap &bmp, COLORREF cTransColor);
47     void    FillWithBitmap(CDC* dc, CBitmap &bmp, RECT rc);
48     void    DrawBitmap(CDC* dc, CBitmap &bmp, RECT rc, DRAW_MODE DrawMode);
49     int        GetBitmapWidth (CBitmap *bmp);
50     int        GetBitmapHeight (CBitmap *bmp);
51     void    RelayEvent(UINT message, WPARAM wParam, LPARAM lParam);
52 private:
53     bool            m_bCheck;
54     DWORD        m_Style;
55     bool            m_bTrack;
56     bool            m_bBtnDown;
57     CToolTipCtrl    m_Tooltip;
58     CBitmap        m_bNormal, m_bDown, m_bDisable, m_bMask, m_bOver, m_bFocus;
59     short            m_nFocusRectMargin;
60     COLORREF    m_cTextColor;
61     HRGN            m_hClipRgn;
62     bool            m_bHasBorder;
63     DRAW_MODE    m_DrawMode;
64 
65     BYTE MinByte(BYTE a, BYTE b) { return (0xff < (a + b) )? 0xff : (a + b); }
66 };
  1 //Source Code
  2 // RgnButton.cpp : 实现文件
  3 //
  4 
  5 #include "stdafx.h"
  6 #include "RegionWnd.h"
  7 #include "RgnButton.h"
  8 
  9 
 10 // CRgnButton
 11 
 12 IMPLEMENT_DYNAMIC(CRgnButton, CButton)
 13 
 14 CRgnButton::CRgnButton()
 15 : m_bCheck(false), m_bBtnDown(false), m_bTrack(false)
 16 , m_DrawMode(DRAW_STRETCH)
 17 , m_hClipRgn(NULL), m_nFocusRectMargin(0)
 18 {
 19     m_cTextColor = GetSysColor(COLOR_BTNTEXT);
 20 }
 21 
 22 CRgnButton::~CRgnButton()
 23 {
 24     if(m_hClipRgn)
 25         DeleteObject(m_hClipRgn);
 26 }
 27 
 28 
 29 BEGIN_MESSAGE_MAP(CRgnButton, CButton)
 30     ON_WM_ERASEBKGND()
 31     ON_WM_LBUTTONDOWN()
 32     ON_WM_LBUTTONUP()
 33     ON_WM_MOUSEMOVE()
 34     ON_WM_LBUTTONDBLCLK()
 35     ON_WM_KILLFOCUS()
 36 //    ON_CONTROL_REFLECT_EX(BN_CLICKED, &CRgnButton::OnBnClicked)
 37     ON_WM_KEYDOWN()
 38 
 39     ON_MESSAGE(WM_MOUSELEAVE,  OnMouseLeave)
 40     ON_MESSAGE(WM_CXSHADE_RADIO,  OnRadioInfo)
 41     ON_MESSAGE(BM_SETCHECK,  OnBMSetCheck)
 42     ON_MESSAGE(BM_GETCHECK,  OnBMGetCheck)
 43 END_MESSAGE_MAP()
 44 
 45 
 46 
 47 // CRgnButton 消息处理程序
 48 
 49 
 50 
 51 void CRgnButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
 52 {
 53     ASSERT(lpDrawItemStruct);
 54 
 55     //Check if the button state is not in inconsistent mode...
 56     POINT mouse_position;
 57     if ( (m_bBtnDown) && (::GetCapture() == m_hWnd) && (::GetCursorPos(&mouse_position))) {
 58         if (::WindowFromPoint(mouse_position) == m_hWnd){
 59             if ((GetState() & BST_PUSHED) != BST_PUSHED) {
 60                 SetState(TRUE);
 61                 return;
 62             }
 63         } else {
 64             if ((GetState() & BST_PUSHED) == BST_PUSHED) {
 65                 SetState(FALSE);
 66                 return;
 67             }
 68         }
 69     }
 70 
 71     CString strCaption;
 72     CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
 73     CRect rc=lpDrawItemStruct->rcItem;
 74     int cx = rc.Width();
 75     int cy = rc.Height();
 76     // get text box position
 77     RECT tr={ rc.left + m_nFocusRectMargin +2, rc.top, rc.right - m_nFocusRectMargin -2, rc.bottom };
 78 
 79     GetWindowText(strCaption);                            // get button text
 80     pDC->SetBkMode(TRANSPARENT);
 81 
 82     // Select the correct skin 
 83     if (lpDrawItemStruct->itemState & ODS_DISABLED){    // DISABLED BUTTON
 84         if(m_bDisable.m_hObject == NULL)
 85             // no skin selected for disabled state -> standard button
 86             pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE));
 87         else // paint the skin
 88             DrawBitmap(pDC, m_bDisable, rc, m_DrawMode);
 89         // if needed, draw the standard 3D rectangular border
 90         if (m_bHasBorder) 
 91             pDC->DrawEdge(&rc, EDGE_RAISED, BF_RECT);
 92         // paint the etched button text
 93         pDC->SetTextColor(GetSysColor(COLOR_3DHILIGHT));
 94         pDC->DrawText(strCaption, &tr, DT_SINGLELINE|DT_VCENTER|DT_CENTER);
 95         pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
 96         OffsetRect(&tr, -1, -1);
 97         pDC->DrawText(strCaption, &tr, DT_SINGLELINE|DT_VCENTER|DT_CENTER);
 98     } else {                                        // SELECTED (DOWN) BUTTON
 99         if ( (lpDrawItemStruct->itemState & ODS_SELECTED) || m_bCheck ) {
100             if(m_bDown.m_hObject==NULL) // no skin selected for selected state -> standard button
101                 pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE));
102             else { // paint the skin
103                 DrawBitmap(pDC, m_bDown, rc, m_DrawMode);
104             }
105             OffsetRect(&tr, 1, 1);  //shift text
106             // if needed, draw the standard 3D rectangular border
107             if (m_bHasBorder) 
108                 pDC->DrawEdge(&rc, EDGE_SUNKEN, BF_RECT);
109         } else {                                            // DEFAULT BUTTON
110             if(m_bNormal.m_hObject==NULL) // no skin selected for normal state -> standard button                
111             {
112                 CString strRect;
113                 strRect.Format(L"Rect: %d, %d, %d, %d\n", rc.left, rc.top, rc.right, rc.bottom);
114                 OutputDebugString(strRect);
115                 pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE));
116             }
117             else if ( (m_bTrack) && (m_bOver.m_hObject != NULL)) { // paint the skin
118                     DrawBitmap(pDC, m_bOver, rc, m_DrawMode);
119             } else {
120                 if ((lpDrawItemStruct->itemState & ODS_FOCUS)&&(m_bFocus.m_hObject != NULL)) {
121                     DrawBitmap(pDC, m_bFocus, rc, m_DrawMode);
122                 } else {
123                     DrawBitmap(pDC, m_bNormal, rc, m_DrawMode);
124                 }
125             }
126             // if needed, draw the standard 3D rectangular border
127             if (m_bHasBorder) 
128                 pDC->DrawEdge(&rc, EDGE_RAISED,BF_RECT);
129         }
130         // paint the focus rect
131         if ((lpDrawItemStruct->itemState & ODS_FOCUS) && (m_nFocusRectMargin > 0)){
132             rc.left   += m_nFocusRectMargin ;
133             rc.top    += m_nFocusRectMargin ;
134             rc.right  -= m_nFocusRectMargin ;
135             rc.bottom -= m_nFocusRectMargin ;
136             DrawFocusRect (lpDrawItemStruct->hDC, &rc) ;
137         }
138         // paint the enabled button text
139         pDC->SetTextColor(m_cTextColor);
140         pDC->DrawText(strCaption,&tr,DT_SINGLELINE|DT_VCENTER|DT_CENTER);
141     }
142 }
143 
144 int CRgnButton::GetBitmapWidth(CBitmap *bmp)
145 {
146     if(!bmp)
147         return -1;
148 
149     BITMAP bm;
150     bmp->GetBitmap(&bm);
151 
152     return bm.bmWidth;
153 }
154 
155 int CRgnButton::GetBitmapHeight(CBitmap *bmp)
156 {
157     if(!bmp)
158         return -1;
159 
160     BITMAP bm;
161     bmp->GetBitmap(&bm);
162 
163     return bm.bmHeight;
164 }
165 
166 void CRgnButton::DrawBitmap(CDC* dc, CBitmap &bmp, RECT rc, DRAW_MODE DrawMode)
167 {
168     if(DrawMode == DRAW_TILED){
169         FillWithBitmap(dc, bmp, rc);
170         return;
171     }
172     if(!bmp.GetSafeHandle()) 
173         return;    //safe check
174 
175     CRect cr = rc;
176     int cx=cr.Width();
177     int cy=cr.Height();
178     CDC dcBmp,dcMask;
179     dcBmp.CreateCompatibleDC(dc);
180     dcBmp.SelectObject(bmp);
181 
182     if (m_bMask.m_hObject!=NULL){
183         dcMask.CreateCompatibleDC(dc);
184         dcMask.SelectObject(m_bMask);
185 
186         CDC dcMem;
187         dcMem.CreateCompatibleDC(dc);
188         CBitmap bmpMem;
189         bmpMem.CreateCompatibleBitmap(dc,cx,cy);
190         CBitmap *oldBmp = dcMem.SelectObject(&bmpMem);
191 
192         dcMem.BitBlt(cr.left, cr.top, cx, cy, dc, 0, 0, SRCCOPY);
193         if(DrawMode == DRAW_NORMAL){
194             dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCINVERT);
195             dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcMask, 0, 0, SRCAND);
196             dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCINVERT);
197         } else {
198             int bx=GetBitmapWidth(&bmp);
199             int by=GetBitmapHeight(&bmp);
200             dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCINVERT);
201             dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcMask, 0, 0, bx, by, SRCAND);
202             dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCINVERT);
203         }
204         dc->BitBlt(cr.left, cr.top, cx, cy, &dcMem, 0, 0, SRCCOPY);
205 
206         dcMem.SelectObject(oldBmp);
207         dcMem.DeleteDC();
208         bmpMem.DeleteObject();
209 
210         DeleteDC(dcMask);
211     } else {
212         if( DrawMode == DRAW_NORMAL){
213             dc->BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCCOPY);
214         } else {
215             int bx=GetBitmapWidth(&bmp);
216             int by=GetBitmapHeight(&bmp);
217             dc->StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCCOPY);
218         }
219     }
220     dcBmp.DeleteDC();
221 }
222 
223 void CRgnButton::FillWithBitmap(CDC* dc, CBitmap &bmp, RECT rc)
224 {
225     if(!bmp.GetSafeHandle()) 
226         return;
227 
228     CDC dcMem;
229     dcMem.CreateCompatibleDC(dc);
230     CBitmap *oldBmp = dcMem.SelectObject(&bmp);
231 
232     int w = rc.right - rc.left;
233     int    h = rc.bottom - rc.top;
234     int x, y, z;
235     int    bx=GetBitmapWidth(&bmp);
236     int    by=GetBitmapHeight(&bmp);
237 
238     for (y = rc.top ; y < h ; y += by){
239         if ( (y + by) > h) 
240             by = h - y;
241         z=bx;
242         for (x = rc.left ; x < w ; x += z){
243             if ( (x + z) > w) 
244                 z = w - x;
245             dc->BitBlt(x, y, z, by, &dcMem, 0, 0, SRCCOPY);
246         }
247     }
248 
249     dcMem.SelectObject(oldBmp);
250     dcMem.DeleteDC();
251 }
252 
253 void CRgnButton::PreSubclassWindow()
254 {
255     m_Style=GetButtonStyle();    ///get specific BS_ styles
256     if ( (m_Style & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX)
257         m_Style=BS_CHECKBOX;
258     else if ((m_Style & BS_AUTORADIOBUTTON)==BS_AUTORADIOBUTTON)
259         m_Style=BS_RADIOBUTTON;
260     else { m_Style=BS_PUSHBUTTON; }
261 
262     CButton::PreSubclassWindow();
263     ModifyStyle(0, BS_OWNERDRAW);
264 }
265 
266 BOOL CRgnButton::OnEraseBkgnd(CDC* pDC)
267 {
268     return TRUE;
269 
270 //    return CButton::OnEraseBkgnd(pDC);
271 }
272 
273 void CRgnButton::OnLButtonDown(UINT nFlags, CPoint point)
274 {
275     RelayEvent(WM_LBUTTONDOWN, (WPARAM)nFlags, MAKELPARAM(LOWORD(point.x), LOWORD(point.y)));
276 
277     //If we are tracking this button, cancel it
278     if (m_bTrack) {
279         TRACKMOUSEEVENT t = {
280             sizeof(TRACKMOUSEEVENT),
281             TME_CANCEL | TME_LEAVE,
282             m_hWnd,
283             0 
284         };
285         if (::_TrackMouseEvent(&t)) {
286             m_bTrack = false;
287         }
288     }
289 
290     CButton::OnLButtonDown(nFlags, point);
291     m_bBtnDown = true;
292 }
293 
294 void CRgnButton::OnLButtonUp(UINT nFlags, CPoint point)
295 {
296     if (m_Style){ //track mouse for radio & check buttons
297         POINT p2 = point;
298         ::ClientToScreen(m_hWnd, &p2);
299         HWND mouse_wnd = ::WindowFromPoint(p2);
300         if (mouse_wnd == m_hWnd){ // mouse is in button
301             if (m_Style==BS_CHECKBOX) 
302                 SetCheck(m_bCheck ? 0 : 1);
303             if (m_Style==BS_RADIOBUTTON) 
304                 SetCheck(1);
305         }
306     }
307     //Pass this message to the ToolTip control
308     RelayEvent(WM_LBUTTONUP,(WPARAM)nFlags,MAKELPARAM(LOWORD(point.x),LOWORD(point.y)));
309 
310     //Default-process the message
311     m_bBtnDown = false;
312     CButton::OnLButtonUp(nFlags, point);
313 }
314 
315 void CRgnButton::OnMouseMove(UINT nFlags, CPoint point)
316 {
317     RelayEvent(WM_MOUSEMOVE,(WPARAM)nFlags,MAKELPARAM(LOWORD(point.x),LOWORD(point.y)));
318 
319     if ( (m_bBtnDown) && (::GetCapture() == m_hWnd)) {
320         POINT p2 = point;
321         ::ClientToScreen(m_hWnd, &p2);
322         HWND mouse_wnd = ::WindowFromPoint(p2);
323 
324         bool bPressed = ((GetState() & BST_PUSHED) == BST_PUSHED);
325         bool bNeedPressed = (mouse_wnd == m_hWnd);
326         if (bPressed != bNeedPressed) {
327             SetState(bNeedPressed ? TRUE : FALSE);
328             Invalidate();
329         }
330     } else {
331         if (!m_bTrack) {
332             TRACKMOUSEEVENT t = {
333                 sizeof(TRACKMOUSEEVENT),
334                 TME_LEAVE,
335                 m_hWnd,
336                 0
337             };
338             if (::_TrackMouseEvent(&t)) {
339                 m_bTrack = true;
340                 Invalidate();
341             }
342         }
343     }
344 
345     CButton::OnMouseMove(nFlags, point);
346 }
347 
348 void CRgnButton::OnLButtonDblClk(UINT nFlags, CPoint point)
349 {
350     SendMessage(WM_LBUTTONDOWN, nFlags, MAKELPARAM(point.x, point.y));
351 
352 //    CButton::OnLButtonDblClk(nFlags, point);
353 }
354 
355 void CRgnButton::OnKillFocus(CWnd* pNewWnd)
356 {
357     if (::GetCapture() == m_hWnd) {
358         ::ReleaseCapture();
359         ASSERT (!m_bTrack);
360         m_bBtnDown = false;
361     }
362 
363     CButton::OnKillFocus(pNewWnd);
364 }
365 
366 BOOL CRgnButton::OnBnClicked()
367 {
368     if (::GetCapture() == m_hWnd) {
369         ::ReleaseCapture();
370         ASSERT (!m_bTrack);
371     }
372 
373     m_bBtnDown = false;
374     return FALSE;
375 }
376 
377 void CRgnButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
378 {
379     if ( (m_Style) && (nChar==' ') ){ //needed stuff for check & radio buttons
380         if (m_Style == BS_CHECKBOX) 
381             SetCheck(m_bCheck ? 0 : 1);
382         if (m_Style == BS_RADIOBUTTON) 
383             SetCheck(1);
384     }
385 
386     CButton::OnKeyDown(nChar, nRepCnt, nFlags);
387 }
388 
389 HRESULT CRgnButton::OnMouseLeave(WPARAM wParam, LPARAM lParam)
390 {
391     ASSERT(m_bTrack);
392 
393     m_bTrack = false;
394     Invalidate();
395 
396     return 0;
397 }
398 
399 HRESULT CRgnButton::OnRadioInfo(WPARAM wParam, LPARAM lParam)
400 {
401     if (m_bCheck){    //only checked buttons need to be unchecked
402         m_bCheck = false;
403         Invalidate();
404     }
405     return 0;
406 }
407 
408 HRESULT CRgnButton::OnBMSetCheck(WPARAM wParam, LPARAM lParam)
409 {
410     m_bCheck = (wParam != 0);
411 
412     switch (m_Style)
413     {
414     case BS_RADIOBUTTON:
415         if (m_bCheck) { //uncheck the other radio buttons (in the same group)
416             HWND hthis,hwnd2,hpwnd;
417             hpwnd=GetParent()->GetSafeHwnd();    //get button parent handle
418             hwnd2=hthis=GetSafeHwnd();            //get this button handle
419             if (hthis && hpwnd){                //consistency check
420                 for( ; ; ){    //scan the buttons within the group
421                     hwnd2=::GetNextDlgGroupItem(hpwnd, hwnd2, 0);
422                     //until we reach again this button
423                     if ( (hwnd2 == hthis) || (hwnd2 == NULL) ) 
424                         break;
425                     //post the uncheck message
426                     ::PostMessage(hwnd2, WM_CXSHADE_RADIO, 0, 0);
427                 }
428             }
429         }
430         break;
431     case BS_PUSHBUTTON:
432         m_bCheck=false;
433         ASSERT(false); // Must be a Check or Radio button to use this function
434     }
435 
436     Invalidate();
437     return 0;
438 }
439 
440 HRESULT CRgnButton::OnBMGetCheck(WPARAM wParam, LPARAM lParam)
441 {
442     return m_bCheck;
443 }
444 
445 void CRgnButton::SetSkin(UINT normal, UINT down, UINT over/* =0 */, 
446              UINT disabled/* =0 */, UINT focus/* =0 */,UINT mask/* =0 */, 
447              DRAW_MODE drawmode/* =1 */,short border/* =1 */,short margin/* =4 */)
448 {
449     m_bNormal.DeleteObject();    //free previous allocated bitmap
450     m_bDown.DeleteObject();
451     m_bOver.DeleteObject();
452     m_bDisable.DeleteObject();
453     m_bMask.DeleteObject();
454     m_bFocus.DeleteObject();
455 
456     if (normal>0) m_bNormal.LoadBitmap(normal);
457     if (down>0)      m_bDown.LoadBitmap(down);
458     if (over>0)      m_bOver.LoadBitmap(over);
459     if (focus>0)  m_bFocus.LoadBitmap(focus);
460 
461     if (disabled>0) 
462         m_bDisable.LoadBitmap(disabled);
463     else if (normal>0) 
464         m_bDisable.LoadBitmap(normal);
465 
466     m_DrawMode = (DRAW_MODE)max(0,min(drawmode, DRAW_TILED));
467     m_bHasBorder = (border > 0);
468     m_nFocusRectMargin = max(0, margin);
469 
470     if (mask>0){
471         m_bMask.LoadBitmap(mask);
472         if (m_hClipRgn) 
473             DeleteObject(m_hClipRgn);
474         m_hClipRgn = CreateRgnFromBitmap(m_bMask, RGB(255,255,255));
475         if (m_hClipRgn){
476             SetWindowRgn(m_hClipRgn, TRUE);
477             GetDC()->SelectClipRgn(CRgn::FromHandle(m_hClipRgn));
478         }
479         if (m_DrawMode == 0){
480             SetWindowPos(NULL, 0, 0, GetBitmapWidth(&m_bMask),
481                 GetBitmapHeight(&m_bMask), SWP_NOZORDER|SWP_NOMOVE);
482         }
483     }
484 }
485 
486 HRGN CRgnButton::CreateRgnFromBitmap(CBitmap &bmp, COLORREF cTransColor)
487 {
488     HRGN hRgn = NULL;
489     COLORREF cTolerance = RGB(0, 0, 0);
490     BITMAP bm;
491 
492     bmp.GetBitmap(&bm);
493     CDC dcMem;
494     dcMem.CreateCompatibleDC(NULL);
495 
496     BITMAPINFOHEADER bInfoHead;
497     bInfoHead.biSize = sizeof(BITMAPINFOHEADER);
498     bInfoHead.biWidth = bm.bmWidth;
499     bInfoHead.biHeight = bm.bmHeight; 
500     bInfoHead.biPlanes = 1; 
501     bInfoHead.biBitCount = 32; 
502     bInfoHead.biCompression = BI_RGB; 
503     bInfoHead.biSizeImage = 0; 
504     bInfoHead.biXPelsPerMeter = 0; 
505     bInfoHead.biYPelsPerMeter = 0; 
506     bInfoHead.biClrUsed = 0; 
507     bInfoHead.biClrImportant = 0; 
508 
509     void *pBit32 = NULL;
510     HBITMAP hBmp32 = CreateDIBSection(dcMem.GetSafeHdc(), (BITMAPINFO *)&bInfoHead, DIB_RGB_COLORS, &pBit32, NULL, 0);
511     if(hBmp32) {
512         CBitmap *pBmp32 = CBitmap::FromHandle(hBmp32);
513         BITMAP bm32;
514         pBmp32->GetBitmap(&bm32);
515         while(bm32.bmWidthBytes % 4)    //round to even
516             bm32.bmWidthBytes++;
517 
518         CBitmap *oldBmp1 = dcMem.SelectObject(pBmp32);
519         CDC dcTmp;
520         dcTmp.CreateCompatibleDC(&dcMem);
521         CBitmap *oldBmp2 = dcTmp.SelectObject(&bmp);
522 
523         dcMem.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcTmp, 0, 0, SRCCOPY);
524 
525         DWORD maxRects = ALLOC_UNIT;  
526         HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects));  
527         RGNDATA *pData = (RGNDATA *)GlobalLock(hData);  
528         pData->rdh.dwSize = sizeof(RGNDATAHEADER);  
529         pData->rdh.iType = RDH_RECTANGLES;  
530         pData->rdh.nCount = pData->rdh.nRgnSize = 0;  
531         SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);
532 
533         BYTE lr = GetRValue(cTransColor);
534         BYTE lg = GetGValue(cTransColor);
535         BYTE lb = GetBValue(cTransColor);
536         BYTE hr = MinByte(lr, GetRValue(cTolerance));
537         BYTE hg = MinByte(lg, GetGValue(cTolerance));
538         BYTE hb = MinByte(lb, GetBValue(cTolerance));
539 
540         BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes;  
541         for (int y = 0; y < bm.bmHeight; y++)  
542         {  
543             // Scan each bitmap pixel from left to right  
544             for (int x = 0; x < bm.bmWidth; x++)  
545             {  
546                 // Search for a continuous range of "non transparent pixels"  
547                 int x0 = x;  
548                 LONG *p = (LONG *)p32 + x;  
549                 while (x < bm.bmWidth)  
550                 {  
551                     BYTE b = GetRValue(*p);  
552                     if (b >= lr && b <= hr)  
553                     {  
554                         b = GetGValue(*p);  
555                         if (b >= lg && b <= hg)  
556                         {  
557                             b = GetBValue(*p);  
558                             if (b >= lb && b <= hb)  
559                                 // This pixel is "transparent"  
560                                 break;  
561                         }  
562                     }  
563                     p++;  
564                     x++;  
565                 }  
566 
567                 if (x > x0)  
568                 {
569                     // Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region  
570                     if (pData->rdh.nCount >= maxRects)  
571                     {  
572                         GlobalUnlock(hData);  
573                         maxRects += ALLOC_UNIT;  
574                         hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE);  
575                         pData = (RGNDATA *)GlobalLock(hData);  
576                     }  
577                     RECT *pr = (RECT *)&pData->Buffer;  
578                     SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1);  
579                     if (x0 < pData->rdh.rcBound.left)  
580                         pData->rdh.rcBound.left = x0;  
581                     if (y < pData->rdh.rcBound.top)  
582                         pData->rdh.rcBound.top = y;  
583                     if (x > pData->rdh.rcBound.right)  
584                         pData->rdh.rcBound.right = x;  
585                     if (y+1 > pData->rdh.rcBound.bottom)  
586                         pData->rdh.rcBound.bottom = y+1;  
587                     pData->rdh.nCount++;  
588 
589                     // On Windows98, ExtCreateRegion() may fail if the number of rectangles is too  
590                     // large (ie: > 4000). Therefore, we have to create the region by multiple steps.  
591                     if (pData->rdh.nCount == 2000)  
592                     {  
593                         HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);  
594                         if (hRgn)  
595                         {  
596                             CombineRgn(hRgn, hRgn, h, RGN_OR);  
597                             DeleteObject(h);  
598                         }  
599                         else 
600                             hRgn = h;  
601                         pData->rdh.nCount = 0;  
602                         SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0);  
603                     }  
604                 }  
605             }  
606 
607             // Go to next row (remember, the bitmap is inverted vertically)  
608             p32 -= bm32.bmWidthBytes;  
609         }  
610 
611         HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData);  
612         if (hRgn)  
613         {  
614             CombineRgn(hRgn, hRgn, h, RGN_OR);  
615             DeleteObject(h);  
616         }  
617         else 
618             hRgn = h;  
619 
620         // Clean up  
621         GlobalFree(hData);
622         dcMem.SelectObject(oldBmp1);
623         DeleteObject(hBmp32);
624         dcTmp.SelectObject(oldBmp2);
625         dcTmp.DeleteDC();
626     }
627     dcMem.DeleteDC();
628 
629     return hRgn;
630 }
631 
632 COLORREF CRgnButton::SetTextColor(COLORREF colorNew)
633 {
634     COLORREF colorTmp = m_cTextColor;
635     m_cTextColor = colorNew;
636 
637     return colorTmp;
638 }
639 
640 void CRgnButton::SetToolTipText(const CString &strTip)
641 {
642     if(m_Tooltip.m_hWnd==NULL){
643         if(m_Tooltip.Create(this))    //first assignment
644             if(m_Tooltip.AddTool(this, strTip))
645                 m_Tooltip.Activate(1);
646     } else {
647         m_Tooltip.UpdateTipText(strTip,this);
648     }
649 }
650 
651 void CRgnButton::RelayEvent(UINT message, WPARAM wParam, LPARAM lParam)
652 {
653     if(NULL != m_Tooltip.m_hWnd){
654         MSG msg;
655         msg.hwnd = m_hWnd;
656         msg.message = message;
657         msg.wParam = wParam;
658         msg.lParam = lParam;
659         msg.time = 0;
660         msg.pt.x = LOWORD(lParam);
661         msg.pt.y = HIWORD(lParam);
662 
663         m_Tooltip.RelayEvent(&msg);
664     }
665 }
posted @ 2012-08-23 17:30  Jojodru  阅读(776)  评论(1编辑  收藏  举报