不规则按钮Button修正版
经过一段时间的debug和修改,现在的接口趋近于完善了,把代码贴上来共享
用法:
1.声明一个按钮变量
CRgnButton m_BtnBall;
2.在父窗口初始化函数中加入如下代码:
m_BtnBall.SubclassDlgItem(IDC_BALL, this); m_BtnBall.SetSkin(IDB_BALL_NORMAL, IDB_BALL_DOWN, IDB_BALL_FOCUS); m_BtnBall.SetToolTipText(_T("Ball"));
这样就可以将对象m_BtnBall和对话框上的按钮关联起来。按钮最少需要3张图片:普通显示,按下去显示,焦点显示。
如果灰度和Mask的图片资源为0,则CRgnButton以Normal为蓝本自动创建这两张图片,以白色作为透明色Mask创建不规则区域。
效果图:
//head file #pragma once // CRgnButton #define WM_CXSHADE_RADIO WM_USER+0x100 #define ALLOC_UNIT 100 class CRgnButton : public CButton { DECLARE_DYNAMIC(CRgnButton) public: CRgnButton(); virtual ~CRgnButton(); enum DRAW_MODE { DRAW_NORMAL, DRAW_STRETCH, DRAW_TILED }; public: void SetToolTipText(const CString &strTip); COLORREF SetTextColor(COLORREF colorNew); void SetSkin(UINT normal, UINT down, UINT over=0, UINT disabled=0, UINT focus=0,UINT mask=0, DRAW_MODE drawmode=DRAW_NORMAL,short border=0,short margin=0); void SetSkin(HBITMAP hNormal, HBITMAP hDown, HBITMAP hOver = NULL, HBITMAP hDisabled = NULL, HBITMAP hFocus = NULL, HBITMAP hMask = NULL, DRAW_MODE drawmode=DRAW_NORMAL,short border=0,short margin=0); public: virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/); protected: virtual void PreSubclassWindow(); afx_msg BOOL OnEraseBkgnd(CDC* pDC); afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnKillFocus(CWnd* pNewWnd); afx_msg BOOL OnBnClicked(); afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags); afx_msg HRESULT OnMouseLeave(WPARAM wParam, LPARAM lParam); afx_msg HRESULT OnRadioInfo(WPARAM wParam, LPARAM lParam); afx_msg HRESULT OnBMSetCheck(WPARAM wParam, LPARAM lParam); afx_msg HRESULT OnBMGetCheck(WPARAM wParam, LPARAM lParam); DECLARE_MESSAGE_MAP() protected: HRGN CreateRgnFromBitmap(CBitmap &bmp, COLORREF cTransColor); void FillWithBitmap(CDC* dc, CBitmap &bmp, RECT rc); void DrawBitmap(CDC* dc, CBitmap &bmp, RECT rc, DRAW_MODE DrawMode); int GetBitmapWidth (CBitmap *bmp); int GetBitmapHeight (CBitmap *bmp); void RelayEvent(UINT message, WPARAM wParam, LPARAM lParam); private: bool m_bCheck; DWORD m_Style; bool m_bTrack; bool m_bBtnDown; CToolTipCtrl m_Tooltip; CBitmap m_bNormal, m_bDown, m_bDisable, m_bMask, m_bOver, m_bFocus; short m_nFocusRectMargin; COLORREF m_cTextColor; HRGN m_hClipRgn; bool m_bHasBorder; DRAW_MODE m_DrawMode; BYTE MinByte(BYTE a, BYTE b) { return (0xff < (a + b) )? 0xff : (a + b); } HBITMAP CreateGrayBmp(CBitmap &bmp); HBITMAP CreateMaskBmp(CBitmap &bmp); };
//implement // RgnButton.cpp : 实现文件 // #include "stdafx.h" #include "RgnButton.h" // CRgnButton IMPLEMENT_DYNAMIC(CRgnButton, CButton) CRgnButton::CRgnButton() : m_bCheck(false), m_bBtnDown(false), m_bTrack(false) , m_DrawMode(DRAW_STRETCH) , m_hClipRgn(NULL), m_nFocusRectMargin(0) { m_cTextColor = GetSysColor(COLOR_BTNTEXT); } CRgnButton::~CRgnButton() { if(m_hClipRgn) DeleteObject(m_hClipRgn); } BEGIN_MESSAGE_MAP(CRgnButton, CButton) ON_WM_ERASEBKGND() ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() ON_WM_LBUTTONDBLCLK() ON_WM_KILLFOCUS() // ON_CONTROL_REFLECT_EX(BN_CLICKED, &CRgnButton::OnBnClicked) ON_WM_KEYDOWN() ON_MESSAGE(WM_MOUSELEAVE, OnMouseLeave) ON_MESSAGE(WM_CXSHADE_RADIO, OnRadioInfo) ON_MESSAGE(BM_SETCHECK, OnBMSetCheck) ON_MESSAGE(BM_GETCHECK, OnBMGetCheck) END_MESSAGE_MAP() // CRgnButton 消息处理程序 void CRgnButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { ASSERT(lpDrawItemStruct); //Check if the button state is not in inconsistent mode... POINT mouse_position; if ( (m_bBtnDown) && (::GetCapture() == m_hWnd) && (::GetCursorPos(&mouse_position))) { if (::WindowFromPoint(mouse_position) == m_hWnd){ if ((GetState() & BST_PUSHED) != BST_PUSHED) { SetState(TRUE); return; } } else { if ((GetState() & BST_PUSHED) == BST_PUSHED) { SetState(FALSE); return; } } } CString strCaption; CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rc=lpDrawItemStruct->rcItem; int cx = rc.Width(); int cy = rc.Height(); // get text box position RECT tr={ rc.left + m_nFocusRectMargin +2, rc.top, rc.right - m_nFocusRectMargin -2, rc.bottom }; GetWindowText(strCaption); // get button text pDC->SetBkMode(TRANSPARENT); // Select the correct skin if (lpDrawItemStruct->itemState & ODS_DISABLED){ // DISABLED BUTTON if(m_bDisable.m_hObject == NULL) // no skin selected for disabled state -> standard button pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE)); else // paint the skin DrawBitmap(pDC, m_bDisable, rc, m_DrawMode); // if needed, draw the standard 3D rectangular border if (m_bHasBorder) pDC->DrawEdge(&rc, EDGE_RAISED, BF_RECT); // paint the etched button text pDC->SetTextColor(GetSysColor(COLOR_3DHILIGHT)); pDC->DrawText(strCaption, &tr, DT_SINGLELINE|DT_VCENTER|DT_CENTER); pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT)); OffsetRect(&tr, -1, -1); pDC->DrawText(strCaption, &tr, DT_SINGLELINE|DT_VCENTER|DT_CENTER); } else { // SELECTED (DOWN) BUTTON if ( (lpDrawItemStruct->itemState & ODS_SELECTED) || m_bCheck ) { if(m_bDown.m_hObject==NULL) // no skin selected for selected state -> standard button pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE)); else { // paint the skin DrawBitmap(pDC, m_bDown, rc, m_DrawMode); } OffsetRect(&tr, 1, 1); //shift text // if needed, draw the standard 3D rectangular border if (m_bHasBorder) pDC->DrawEdge(&rc, EDGE_SUNKEN, BF_RECT); } else { // DEFAULT BUTTON if(m_bNormal.m_hObject==NULL) // no skin selected for normal state -> standard button { CString strRect; strRect.Format(L"Rect: %d, %d, %d, %d\n", rc.left, rc.top, rc.right, rc.bottom); OutputDebugString(strRect); pDC->FillSolidRect(&rc, GetSysColor(COLOR_BTNFACE)); } else if ( (m_bTrack) && (m_bOver.m_hObject != NULL)) { // paint the skin DrawBitmap(pDC, m_bOver, rc, m_DrawMode); } else { if ((lpDrawItemStruct->itemState & ODS_FOCUS)&&(m_bFocus.m_hObject != NULL)) { DrawBitmap(pDC, m_bFocus, rc, m_DrawMode); } else { DrawBitmap(pDC, m_bNormal, rc, m_DrawMode); } } // if needed, draw the standard 3D rectangular border if (m_bHasBorder) pDC->DrawEdge(&rc, EDGE_RAISED,BF_RECT); } // paint the focus rect if ((lpDrawItemStruct->itemState & ODS_FOCUS) && (m_nFocusRectMargin > 0)){ rc.left += m_nFocusRectMargin ; rc.top += m_nFocusRectMargin ; rc.right -= m_nFocusRectMargin ; rc.bottom -= m_nFocusRectMargin ; DrawFocusRect (lpDrawItemStruct->hDC, &rc) ; } // paint the enabled button text pDC->SetTextColor(m_cTextColor); pDC->DrawText(strCaption,&tr,DT_SINGLELINE|DT_VCENTER|DT_CENTER); } } int CRgnButton::GetBitmapWidth(CBitmap *bmp) { if(!bmp) return -1; BITMAP bm; bmp->GetBitmap(&bm); return bm.bmWidth; } int CRgnButton::GetBitmapHeight(CBitmap *bmp) { if(!bmp) return -1; BITMAP bm; bmp->GetBitmap(&bm); return bm.bmHeight; } void CRgnButton::DrawBitmap(CDC* dc, CBitmap &bmp, RECT rc, DRAW_MODE DrawMode) { if(DrawMode == DRAW_TILED){ FillWithBitmap(dc, bmp, rc); return; } if(!bmp.GetSafeHandle()) return; //safe check CRect cr = rc; int cx=cr.Width(); int cy=cr.Height(); CDC dcBmp,dcMask; dcBmp.CreateCompatibleDC(dc); dcBmp.SelectObject(bmp); if (m_bMask.m_hObject!=NULL){ dcMask.CreateCompatibleDC(dc); dcMask.SelectObject(m_bMask); CDC dcMem; dcMem.CreateCompatibleDC(dc); CBitmap bmpMem; bmpMem.CreateCompatibleBitmap(dc,cx,cy); CBitmap *oldBmp = dcMem.SelectObject(&bmpMem); dcMem.BitBlt(cr.left, cr.top, cx, cy, dc, 0, 0, SRCCOPY); if(DrawMode == DRAW_NORMAL){ dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCINVERT); dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcMask, 0, 0, SRCAND); dcMem.BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCINVERT); } else { int bx=GetBitmapWidth(&bmp); int by=GetBitmapHeight(&bmp); dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCINVERT); dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcMask, 0, 0, bx, by, SRCAND); dcMem.StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCINVERT); } dc->BitBlt(cr.left, cr.top, cx, cy, &dcMem, 0, 0, SRCCOPY); dcMem.SelectObject(oldBmp); dcMem.DeleteDC(); bmpMem.DeleteObject(); DeleteDC(dcMask); } else { if( DrawMode == DRAW_NORMAL){ dc->BitBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, SRCCOPY); } else { int bx=GetBitmapWidth(&bmp); int by=GetBitmapHeight(&bmp); dc->StretchBlt(cr.left, cr.top, cx, cy, &dcBmp, 0, 0, bx, by, SRCCOPY); } } dcBmp.DeleteDC(); } void CRgnButton::FillWithBitmap(CDC* dc, CBitmap &bmp, RECT rc) { if(!bmp.GetSafeHandle()) return; CDC dcMem; dcMem.CreateCompatibleDC(dc); CBitmap *oldBmp = dcMem.SelectObject(&bmp); int w = rc.right - rc.left; int h = rc.bottom - rc.top; int x, y, z; int bx=GetBitmapWidth(&bmp); int by=GetBitmapHeight(&bmp); for (y = rc.top ; y < h ; y += by){ if ( (y + by) > h) by = h - y; z=bx; for (x = rc.left ; x < w ; x += z){ if ( (x + z) > w) z = w - x; dc->BitBlt(x, y, z, by, &dcMem, 0, 0, SRCCOPY); } } dcMem.SelectObject(oldBmp); dcMem.DeleteDC(); } void CRgnButton::PreSubclassWindow() { m_Style=GetButtonStyle(); ///get specific BS_ styles if ( (m_Style & BS_AUTOCHECKBOX) == BS_AUTOCHECKBOX) m_Style=BS_CHECKBOX; else if ((m_Style & BS_AUTORADIOBUTTON)==BS_AUTORADIOBUTTON) m_Style=BS_RADIOBUTTON; else { m_Style=BS_PUSHBUTTON; } CButton::PreSubclassWindow(); ModifyStyle(0, BS_OWNERDRAW); } BOOL CRgnButton::OnEraseBkgnd(CDC* pDC) { return TRUE; // return CButton::OnEraseBkgnd(pDC); } void CRgnButton::OnLButtonDown(UINT nFlags, CPoint point) { RelayEvent(WM_LBUTTONDOWN, (WPARAM)nFlags, MAKELPARAM(LOWORD(point.x), LOWORD(point.y))); //If we are tracking this button, cancel it if (m_bTrack) { TRACKMOUSEEVENT t = { sizeof(TRACKMOUSEEVENT), TME_CANCEL | TME_LEAVE, m_hWnd, 0 }; if (::_TrackMouseEvent(&t)) { m_bTrack = false; } } CButton::OnLButtonDown(nFlags, point); m_bBtnDown = true; } void CRgnButton::OnLButtonUp(UINT nFlags, CPoint point) { if (m_Style){ //track mouse for radio & check buttons POINT p2 = point; ::ClientToScreen(m_hWnd, &p2); HWND mouse_wnd = ::WindowFromPoint(p2); if (mouse_wnd == m_hWnd){ // mouse is in button if (m_Style==BS_CHECKBOX) SetCheck(m_bCheck ? 0 : 1); if (m_Style==BS_RADIOBUTTON) SetCheck(1); } } //Pass this message to the ToolTip control RelayEvent(WM_LBUTTONUP,(WPARAM)nFlags,MAKELPARAM(LOWORD(point.x),LOWORD(point.y))); //Default-process the message m_bBtnDown = false; CButton::OnLButtonUp(nFlags, point); } void CRgnButton::OnMouseMove(UINT nFlags, CPoint point) { RelayEvent(WM_MOUSEMOVE,(WPARAM)nFlags,MAKELPARAM(LOWORD(point.x),LOWORD(point.y))); if ( (m_bBtnDown) && (::GetCapture() == m_hWnd)) { POINT p2 = point; ::ClientToScreen(m_hWnd, &p2); HWND mouse_wnd = ::WindowFromPoint(p2); bool bPressed = ((GetState() & BST_PUSHED) == BST_PUSHED); bool bNeedPressed = (mouse_wnd == m_hWnd); if (bPressed != bNeedPressed) { SetState(bNeedPressed ? TRUE : FALSE); Invalidate(); } } else { if (!m_bTrack) { TRACKMOUSEEVENT t = { sizeof(TRACKMOUSEEVENT), TME_LEAVE, m_hWnd, 0 }; if (::_TrackMouseEvent(&t)) { m_bTrack = true; Invalidate(); } } } CButton::OnMouseMove(nFlags, point); } void CRgnButton::OnLButtonDblClk(UINT nFlags, CPoint point) { SendMessage(WM_LBUTTONDOWN, nFlags, MAKELPARAM(point.x, point.y)); // CButton::OnLButtonDblClk(nFlags, point); } void CRgnButton::OnKillFocus(CWnd* pNewWnd) { if (::GetCapture() == m_hWnd) { ::ReleaseCapture(); ASSERT (!m_bTrack); m_bBtnDown = false; } CButton::OnKillFocus(pNewWnd); } BOOL CRgnButton::OnBnClicked() { if (::GetCapture() == m_hWnd) { ::ReleaseCapture(); ASSERT (!m_bTrack); } m_bBtnDown = false; return FALSE; } void CRgnButton::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { if ( (m_Style) && (nChar==' ') ){ //needed stuff for check & radio buttons if (m_Style == BS_CHECKBOX) SetCheck(m_bCheck ? 0 : 1); if (m_Style == BS_RADIOBUTTON) SetCheck(1); } CButton::OnKeyDown(nChar, nRepCnt, nFlags); } HRESULT CRgnButton::OnMouseLeave(WPARAM wParam, LPARAM lParam) { ASSERT(m_bTrack); m_bTrack = false; Invalidate(); return 0; } HRESULT CRgnButton::OnRadioInfo(WPARAM wParam, LPARAM lParam) { if (m_bCheck){ //only checked buttons need to be unchecked m_bCheck = false; Invalidate(); } return 0; } HRESULT CRgnButton::OnBMSetCheck(WPARAM wParam, LPARAM lParam) { m_bCheck = (wParam != 0); switch (m_Style) { case BS_RADIOBUTTON: if (m_bCheck) { //uncheck the other radio buttons (in the same group) HWND hthis,hwnd2,hpwnd; hpwnd=GetParent()->GetSafeHwnd(); //get button parent handle hwnd2=hthis=GetSafeHwnd(); //get this button handle if (hthis && hpwnd){ //consistency check for( ; ; ){ //scan the buttons within the group hwnd2=::GetNextDlgGroupItem(hpwnd, hwnd2, 0); //until we reach again this button if ( (hwnd2 == hthis) || (hwnd2 == NULL) ) break; //post the uncheck message ::PostMessage(hwnd2, WM_CXSHADE_RADIO, 0, 0); } } } break; case BS_PUSHBUTTON: m_bCheck=false; ASSERT(false); // Must be a Check or Radio button to use this function } Invalidate(); return 0; } HRESULT CRgnButton::OnBMGetCheck(WPARAM wParam, LPARAM lParam) { return m_bCheck; } void CRgnButton::SetSkin(UINT normal, UINT down, UINT over/* =0 */, UINT disabled/* =0 */, UINT focus/* =0 */,UINT mask/* =0 */, DRAW_MODE drawmode/* =1 */,short border/* =1 */,short margin/* =4 */) { m_bNormal.DeleteObject(); //free previous allocated bitmap m_bDown.DeleteObject(); m_bOver.DeleteObject(); m_bDisable.DeleteObject(); m_bMask.DeleteObject(); m_bFocus.DeleteObject(); if (normal > 0) m_bNormal.LoadBitmap(normal); if (down > 0) m_bDown.LoadBitmap(down); if (over > 0) m_bOver.LoadBitmap(over); if (focus > 0) m_bFocus.LoadBitmap(focus); if (disabled > 0) m_bDisable.LoadBitmap(disabled); else if (normal > 0) m_bDisable.Attach(CreateGrayBmp(m_bNormal)); m_DrawMode = (DRAW_MODE)max(0,min(drawmode, DRAW_TILED)); m_bHasBorder = (border > 0); m_nFocusRectMargin = max(0, margin); if (mask > 0) m_bMask.LoadBitmap(mask); else if(normal > 0) m_bMask.Attach(CreateMaskBmp(m_bNormal)); if (mask > 0 || normal > 0){ if (m_hClipRgn) DeleteObject(m_hClipRgn); m_hClipRgn = CreateRgnFromBitmap(m_bMask, RGB(255,255,255)); if (m_hClipRgn){ SetWindowRgn(m_hClipRgn, TRUE); GetDC()->SelectClipRgn(CRgn::FromHandle(m_hClipRgn)); } if (m_DrawMode == 0){ SetWindowPos(NULL, 0, 0, GetBitmapWidth(&m_bMask), GetBitmapHeight(&m_bMask), SWP_NOZORDER|SWP_NOMOVE); } } } void CRgnButton::SetSkin(HBITMAP hNormal, HBITMAP hDown, HBITMAP hOver /* = NULL */, HBITMAP hDisabled /* = NULL */, HBITMAP hFocus /* = NULL */, HBITMAP hMask /* = NULL */, DRAW_MODE drawmode/* =DRAW_NORMAL */,short border/* =0 */,short margin/* =0 */) { m_bNormal.DeleteObject(); //free previous allocated bitmap m_bDown.DeleteObject(); m_bOver.DeleteObject(); m_bDisable.DeleteObject(); m_bMask.DeleteObject(); m_bFocus.DeleteObject(); if (hNormal != NULL) m_bNormal.Attach(hNormal); if (hDown != NULL) m_bDown.Attach(hDown); if (hOver != NULL) m_bOver.Attach(hOver); if (hFocus != NULL) m_bFocus.Attach(hFocus); if (hDisabled != NULL) m_bDisable.Attach(hDisabled); else if (hNormal != NULL) m_bDisable.Attach(CreateGrayBmp(m_bNormal)); m_DrawMode = (DRAW_MODE)max(0,min(drawmode, DRAW_TILED)); m_bHasBorder = (border > 0); m_nFocusRectMargin = max(0, margin); if(hMask) m_bMask.Attach(hMask); else if(hNormal) m_bMask.Attach(CreateMaskBmp(m_bNormal)); if (hMask || hNormal){ if (m_hClipRgn) DeleteObject(m_hClipRgn); m_hClipRgn = CreateRgnFromBitmap(m_bMask, RGB(255,255,255)); if (m_hClipRgn){ SetWindowRgn(m_hClipRgn, TRUE); GetDC()->SelectClipRgn(CRgn::FromHandle(m_hClipRgn)); } if (m_DrawMode == 0){ SetWindowPos(NULL, 0, 0, GetBitmapWidth(&m_bMask), GetBitmapHeight(&m_bMask), SWP_NOZORDER|SWP_NOMOVE); } } } HRGN CRgnButton::CreateRgnFromBitmap(CBitmap &bmp, COLORREF cTransColor) { HRGN hRgn = NULL; COLORREF cTolerance = RGB(0, 0, 0); BITMAP bm; bmp.GetBitmap(&bm); CDC dcMem; dcMem.CreateCompatibleDC(NULL); BITMAPINFOHEADER bInfoHead; bInfoHead.biSize = sizeof(BITMAPINFOHEADER); bInfoHead.biWidth = bm.bmWidth; bInfoHead.biHeight = bm.bmHeight; bInfoHead.biPlanes = 1; bInfoHead.biBitCount = 32; bInfoHead.biCompression = BI_RGB; bInfoHead.biSizeImage = 0; bInfoHead.biXPelsPerMeter = 0; bInfoHead.biYPelsPerMeter = 0; bInfoHead.biClrUsed = 0; bInfoHead.biClrImportant = 0; void *pBit32 = NULL; HBITMAP hBmp32 = CreateDIBSection(dcMem.GetSafeHdc(), (BITMAPINFO *)&bInfoHead, DIB_RGB_COLORS, &pBit32, NULL, 0); if(hBmp32) { CBitmap *pBmp32 = CBitmap::FromHandle(hBmp32); BITMAP bm32; pBmp32->GetBitmap(&bm32); while(bm32.bmWidthBytes % 4) //round to even bm32.bmWidthBytes++; CBitmap *oldBmp1 = dcMem.SelectObject(pBmp32); CDC dcTmp; dcTmp.CreateCompatibleDC(&dcMem); CBitmap *oldBmp2 = dcTmp.SelectObject(&bmp); dcMem.BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dcTmp, 0, 0, SRCCOPY); DWORD maxRects = ALLOC_UNIT; HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects)); RGNDATA *pData = (RGNDATA *)GlobalLock(hData); pData->rdh.dwSize = sizeof(RGNDATAHEADER); pData->rdh.iType = RDH_RECTANGLES; pData->rdh.nCount = pData->rdh.nRgnSize = 0; SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); BYTE lr = GetRValue(cTransColor); BYTE lg = GetGValue(cTransColor); BYTE lb = GetBValue(cTransColor); BYTE hr = MinByte(lr, GetRValue(cTolerance)); BYTE hg = MinByte(lg, GetGValue(cTolerance)); BYTE hb = MinByte(lb, GetBValue(cTolerance)); BYTE *p32 = (BYTE *)bm32.bmBits + (bm32.bmHeight - 1) * bm32.bmWidthBytes; for (int y = 0; y < bm.bmHeight; y++) { // Scan each bitmap pixel from left to right for (int x = 0; x < bm.bmWidth; x++) { // Search for a continuous range of "non transparent pixels" int x0 = x; LONG *p = (LONG *)p32 + x; while (x < bm.bmWidth) { BYTE b = GetRValue(*p); if (b >= lr && b <= hr) { b = GetGValue(*p); if (b >= lg && b <= hg) { b = GetBValue(*p); if (b >= lb && b <= hb) // This pixel is "transparent" break; } } p++; x++; } if (x > x0) { // Add the pixels (x0, y) to (x, y+1) as a new rectangle in the region if (pData->rdh.nCount >= maxRects) { GlobalUnlock(hData); maxRects += ALLOC_UNIT; hData = GlobalReAlloc(hData, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), GMEM_MOVEABLE); pData = (RGNDATA *)GlobalLock(hData); } RECT *pr = (RECT *)&pData->Buffer; SetRect(&pr[pData->rdh.nCount], x0, y, x, y+1); if (x0 < pData->rdh.rcBound.left) pData->rdh.rcBound.left = x0; if (y < pData->rdh.rcBound.top) pData->rdh.rcBound.top = y; if (x > pData->rdh.rcBound.right) pData->rdh.rcBound.right = x; if (y+1 > pData->rdh.rcBound.bottom) pData->rdh.rcBound.bottom = y+1; pData->rdh.nCount++; // On Windows98, ExtCreateRegion() may fail if the number of rectangles is too // large (ie: > 4000). Therefore, we have to create the region by multiple steps. if (pData->rdh.nCount == 2000) { HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData); if (hRgn) { CombineRgn(hRgn, hRgn, h, RGN_OR); DeleteObject(h); } else hRgn = h; pData->rdh.nCount = 0; SetRect(&pData->rdh.rcBound, MAXLONG, MAXLONG, 0, 0); } } } // Go to next row (remember, the bitmap is inverted vertically) p32 -= bm32.bmWidthBytes; } HRGN h = ExtCreateRegion(NULL, sizeof(RGNDATAHEADER) + (sizeof(RECT) * maxRects), pData); if (hRgn) { CombineRgn(hRgn, hRgn, h, RGN_OR); DeleteObject(h); } else hRgn = h; // Clean up GlobalFree(hData); dcMem.SelectObject(oldBmp1); DeleteObject(hBmp32); dcTmp.SelectObject(oldBmp2); dcTmp.DeleteDC(); } dcMem.DeleteDC(); return hRgn; } COLORREF CRgnButton::SetTextColor(COLORREF colorNew) { COLORREF colorTmp = m_cTextColor; m_cTextColor = colorNew; return colorTmp; } void CRgnButton::SetToolTipText(const CString &strTip) { if(m_Tooltip.m_hWnd==NULL){ if(m_Tooltip.Create(this)) //first assignment if(m_Tooltip.AddTool(this, strTip)) m_Tooltip.Activate(1); } else { m_Tooltip.UpdateTipText(strTip,this); } } void CRgnButton::RelayEvent(UINT message, WPARAM wParam, LPARAM lParam) { if(NULL != m_Tooltip.m_hWnd){ MSG msg; msg.hwnd = m_hWnd; msg.message = message; msg.wParam = wParam; msg.lParam = lParam; msg.time = 0; msg.pt.x = LOWORD(lParam); msg.pt.y = HIWORD(lParam); m_Tooltip.RelayEvent(&msg); } } HBITMAP CRgnButton::CreateMaskBmp(CBitmap &bmp) { BITMAP bmSrcInfo; bmp.GetBitmap(&bmSrcInfo); CDC memDC; CDC maskDC; CBitmap bmpMask; memDC.CreateCompatibleDC(NULL); maskDC.CreateCompatibleDC(NULL); bmpMask.CreateBitmap(bmSrcInfo.bmWidth, bmSrcInfo.bmHeight, 1, 1, NULL); CBitmap *pOldBmp1 = maskDC.SelectObject(&bmpMask); CBitmap *pOldBmp2 = memDC.SelectObject(&bmp); memDC.SetBkColor(RGB(0xff,0xff,0xff)); maskDC.BitBlt(0, 0, bmSrcInfo.bmWidth, bmSrcInfo.bmHeight, &memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOldBmp1); memDC.SelectObject(pOldBmp2); memDC.DeleteDC(); maskDC.DeleteDC(); return (HBITMAP)bmpMask.Detach(); } HBITMAP CRgnButton::CreateGrayBmp(CBitmap &bmp) { BITMAP bmSrcInfo; bmp.GetBitmap(&bmSrcInfo); int height = bmSrcInfo.bmHeight; int width = bmSrcInfo.bmWidth; int widthBytes = bmSrcInfo.bmWidthBytes; CDC *pDC = GetDC(); HBITMAP hRet = CreateCompatibleBitmap(pDC->GetSafeHdc(), width, height); if(!hRet) return NULL; CBitmap *bmpSrc = CBitmap::FromHandle(hRet); CDC dcMem; CDC dcTmp; dcMem.CreateCompatibleDC(NULL); dcTmp.CreateCompatibleDC(NULL); CBitmap *oldBmp1 = dcTmp.SelectObject(&bmp); CBitmap *oldBmp2 = dcMem.SelectObject(bmpSrc); dcMem.BitBlt(0, 0, width, height, &dcTmp, 0, 0,SRCCOPY); dcTmp.SelectObject(oldBmp1); dcTmp.DeleteDC(); dcMem.SelectObject(oldBmp2); dcMem.DeleteDC(); ReleaseDC(pDC); //Gray process bmpSrc->GetBitmap( &bmSrcInfo ); DWORD dwBmByteSize = bmSrcInfo.bmWidthBytes * bmSrcInfo.bmHeight; LPBYTE pBmBits = (LPBYTE) GlobalAlloc(GPTR, dwBmByteSize); bmpSrc->GetBitmapBits(dwBmByteSize, pBmBits); int nPixBytes = bmSrcInfo.bmBitsPixel/8; if(nPixBytes > 1) { int nRemainLineBytes = bmSrcInfo.bmWidthBytes - bmSrcInfo.bmWidth * nPixBytes; //bytes left on every line DWORD dwOffset = 0; for(long nHeight=0; nHeight<bmSrcInfo.bmHeight; nHeight++) { for(long nWidth=0; nWidth<bmSrcInfo.bmWidth; nWidth++) { BYTE cR = pBmBits[dwOffset]; BYTE cG = pBmBits[dwOffset + 1]; BYTE cB = pBmBits[dwOffset + 2]; int nPer; if (cR == 0xff && cG == 0xff && cB == 0xff) nPer = 0xff; else nPer = (int )(cR * 0.299 + cG * 0.587 + cB * 0.114); for(int np2=0; np2<nPixBytes; np2++) { pBmBits[dwOffset + np2] = nPer; } dwOffset += nPixBytes; } dwOffset += nRemainLineBytes; } } bmpSrc->SetBitmapBits(dwBmByteSize, pBmBits); GlobalFree(pBmBits); return hRet; }