不规则按钮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;
}
posted @ 2012-09-05 11:10  Jojodru  阅读(2237)  评论(0编辑  收藏  举报