VC不规则窗口实现—位图区域裁剪

既然默认创建的窗口是矩形区域,只要把不想显示的区域透明化,剩下的不就是不规则窗口了吗?!接下来所谓的窗口透明化,我们直接使用CRgn类来实现创建、裁剪和合并区域的功能,然后再使用SetWindowRgn函数来进行整个窗体的区域绘制。

    不规则窗口,顾名思义,就是窗口的形状不是长方形、正方形或者圆形,而是一些不规则的图形。例如我们常常看到的一些可设置皮肤的窗口,机器人形状,五角星图形窗口一样等等。那应该如何实现呢?窗口贴图?
    OK,就算我们采用窗口贴图的方式,默认的窗口创建还是只会显示规规矩矩的矩形对话框,无法实现该效果。可世上无难事,聪明的人总会能找到方法。根据网上提供的方法,在贴图的基础上,我们采用创建、裁剪和合并区域的方法来进行不规则窗口的创建。
    在这里,我们要实现下图为例来讲解一下实现方法。
VC不规则窗口实现—位图区域裁剪_第1张图片

解题思路:
    既然默认创建的窗口是矩形区域,只要把不想显示的区域透明化,剩下的不就是不规则窗口了吗?!接下来所谓的窗口透明化,我们直接使用CRgn类来实现创建、裁剪和合并区域的功能,然后再使用SetWindowRgn函数来进行整个窗体的区域绘制。
    SetWindowRgn 函数是设置了一个窗口的区域。只有被包含在这个区域内的地方才会被重绘,而不包含在区域内的其他区域系统将不会显示。
    于是要求设计MM给我把要去掉的位图区域加上纯红颜色,如下图所示:
VC不规则窗口实现—位图区域裁剪_第2张图片

1.把位图中不想显示的区域用另外的颜色填充,上图使用了红色RGB(255,0,0);
2.加载位图到对话框中,然后创建一个初始为0的区域;
3.以一个像素点为单位,从位图的最左边(0,0)位置开始扫描,然后到右下角(width,height)结束;
4.如果遇到是一个纯红色像素的,跳过,继续扫描;如果下一个遇到不是纯红色像素,则记录该点X1坐标,然后继续扫描;直到遇到纯红色像素,此时记录下该点X2坐标,用CreateRectRgn创建一个像素高的(x1,y,x2,y+1)区域,然后用CombineRgn与原先按要求创建好的区域进行合并,如此类推;
5.最后使用SetWindowRgn函数设置最终的窗体区域,成功。

  ps:因为用PS填充的时候,红色和白色相交的地方会出现颜色渐变的情况,注意此时应该一个点一个点地用纯红色填充(RGB(255,0,0))同时程序中的颜色取值应使用范围值。

代码:

void CIrregularWindowDlg::OnPaint() 
{
    CPaintDC dc(this); // device context for painting
    CDC picDC,memDC; 
    CBitmap *pOldBmp;
    BITMAP bm;

    CRgn wndRgn;  
    CRgn rgnTemp; //保存临时region 

    int iX=0, iLeftX=0;

    CRect rcClient, rtWindow;
    CFont *pOldFont;

    GetClientRect(rcClient);

    picDC.CreateCompatibleDC (&dc);   
    pOldBmp = picDC.SelectObject (&m_bitmap); 

    //没加载图片成功,退出
    if (pOldBmp==NULL)
    {
        OnCancel();
    }

    m_bitmap.GetBitmap(&bm);

    //创建总的窗体区域,初始region为0
    wndRgn.CreateRectRgn(0,0,0,0);        

    for(int y=0; y<=bm.bmHeight; y++)
    {
        iX = 0;
        do
        {
            //跳过透明色找到下一个非透明色的点.
            while (iX <= bm.bmWidth  && (picDC.GetPixel(iX, y) >= m_TargetStartColor && picDC.GetPixel(iX, y) <= m_TargetEndColor))
                iX++; 

            //记住这个起始点
            iLeftX = iX;       

            //寻找下个透明色的点
            while (iX <= bm.bmWidth  && (picDC.GetPixel(iX, y) < m_TargetStartColor || picDC.GetPixel(iX, y) > m_TargetEndColor))
                ++iX;   

            //创建一个包含起点与重点间高为1像素的临时"region"
            rgnTemp.CreateRectRgn(iLeftX, y, iX, y+1);   

            //合并到主"region".
            wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_OR);   

            //删除临时"region",否则下次创建时和出错
            rgnTemp.DeleteObject();
        }while(iX <= bm.bmWidth);
    }

    CWnd * pWnd = dc.GetWindow();
    pWnd->SetWindowRgn(wndRgn,TRUE);    
    pWnd->SetForegroundWindow();  

    pOldFont = picDC.SelectObject(m_pSysFont);
    if (m_pTargetFont!=NULL)
    {
        picDC.SelectObject(m_pTargetFont);
    }
    picDC.SetBkMode(TRANSPARENT);
    picDC.SetTextColor(m_clTargetColor);
    picDC.DrawText(m_strText, m_rcText, DT_WORDBREAK);
    picDC.SelectObject(pOldFont);

    dc.BitBlt (0, 0, bm.bmWidth, bm.bmHeight, &picDC, 0, 0, SRCCOPY);
    dc.SelectObject(pOldBmp);
}

void CIrregularWindowDlg::InitDialogInfo()
{
    if (!m_bitmap.LoadBitmap(m_uiResourceID))
    {
        CDialog::OnCancel();
        return;
    }

    BITMAP bm;
    CRect rtWindow;

    m_bitmap.GetBitmap(&bm);

    GetWindowRect(&rtWindow);
    rtWindow.right = rtWindow.left+bm.bmWidth;
    rtWindow.bottom =rtWindow.top +bm.bmHeight;
    MoveWindow(&rtWindow);
}

void CIrregularWindowDlg::SetReplacePicColorRange(COLORREF TargetStartColor, COLORREF TargetEndColor)
{
    m_TargetStartColor = TargetStartColor;
    m_TargetEndColor = TargetEndColor;
}

/************************************************************************/
/* 加载位图
/************************************************************************/
BOOL CIrregularWindowDlg::SetLoadBitmap(UINT BitmapID)
{
    if (BitmapID>0)
    {
        m_uiResourceID = BitmapID;
    }
    else
    {
        return FALSE;
    }

    return TRUE;
}

/************************************************************************/
/* 
设置对话框显示的文字,可支持换行\n
参数:
    rcText:文字位置,CRect类型
    lstrText:显示的内容
/************************************************************************/
void CIrregularWindowDlg::SetDrawText(CRect rcText, CString lstrText)
{
    m_rcText = rcText;
    m_strText = _T("");
    m_strText = lstrText;
}

/************************************************************************/
/* 
设置对话框显示的文字字体
参数:
    fontObject:字体指针
/************************************************************************/
void CIrregularWindowDlg::SetDrawTextFont(CFont *fontObject)
{
    if (fontObject!=NULL)
    {
        m_pTargetFont = fontObject;
    }
}

/************************************************************************/
/* 
设置对话框显示的文字字体颜色
参数:
    clValue:目标颜色
/************************************************************************/
void CIrregularWindowDlg::SetDrawTextColor(COLORREF clTargetValue)
{
    m_clTargetColor = clTargetValue;
}

void CIrregularWindowDlg::OnClose() 
{
    // TODO: Add your message handler code here and/or call default
    // 删除字体
    if (m_pSysFont)
    {
        m_pSysFont->DeleteObject();
        delete m_pSysFont;
        m_pSysFont = NULL;
    }

    if (m_pTargetFont)
    {
        m_pTargetFont->DeleteObject();
        delete m_pTargetFont;
        m_pTargetFont = NULL;
    }

    CDialog::OnClose();
}

调用代码:

// 系统默认字体
    m_pSysFont = new CFont; 
    m_pSysFont->CreateFont(12, // nHeight 
        0, // nWidth 
        0, // nEscapement 
        0, // nOrientation 
        FW_NORMAL, // nWeight 
        FALSE, // bItalic 
        FALSE, // bUnderline 
        0, // cStrikeOut 
        ANSI_CHARSET, // nCharSet 
        OUT_DEFAULT_PRECIS, // nOutPrecision 
        CLIP_DEFAULT_PRECIS, // nClipPrecision 
        DEFAULT_QUALITY, // nQuality 
        DEFAULT_PITCH | FF_SWISS, // nPitchAndFamily 
        _T("宋体")); // lpszFac

m_pIrrDlg = new CIrregularWindowDlg;    
    ASSERT(m_pIrrDlg);
    if (!m_pIrrDlg->SetLoadBitmap(IDB_IRREGULAR_PICTURE))
    {
        return FALSE;
    }

    m_pIrrDlg->SetDrawTextFont(m_pSysFont);
    m_pIrrDlg->SetDrawTextColor(RGB(95,107,122));
    m_pIrrDlg->SetReplacePicColorRange(RGB(200,5,5), RGB(255,5,5));
    m_pIrrDlg->Create(CIrregularWindowDlg::IDD, this);
    m_pIrrDlg->ShowWindow(SW_HIDE);

    ps:这里的对话框创建不要使用DoModal (),因为它是模态对话框的。为了能够隐藏和灵活调用显示对话框,我们应该使用Create来创建对话框。
    OK,完工。

你可能感兴趣的:(VC)