使用白色作为透明色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 }