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;
    }
  • 相关阅读:
    python3-基础11
    python3-基础10
    python3-基础9
    python3-基础8
    python3-基础7
    python3-基础6
    phaserjs 总结
    ES6总结
    移动端webview调试
    nodejs的理解
  • 原文地址:https://www.cnblogs.com/jojodru/p/2671568.html
Copyright © 2011-2022 走看看