zoukankan      html  css  js  c++  java
  • 不规则按钮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;
    }
  • 相关阅读:
    C# 操作配置文件
    C# Excel操作类
    没有找到 mspdb100.dll 的解决办法
    工厂方法模式
    .Net互操作2
    The certificate used to sign “AppName” has either expired or has been revoked. An updated certificate is required to sign and install the application解决
    手机抓包xcode自带命令行工具配合wireshark实现
    expecting SSH2_MSG_KEX_ECDH_REPLY ssh_dispatch_run_fatal问题解决
    使用ssh-keygen设置ssh无密码登录
    远程复制文件到服务器
  • 原文地址:https://www.cnblogs.com/jojodru/p/2671568.html
Copyright © 2011-2022 走看看