来源:http://blog.youkuaiyun.com/enin_dqc/archive/2010/09/09/5874327.aspx
常见的按钮或窗体都是四四方方的矩形,默认创建产生的window窗体都是矩形的。但非矩形窗体在Windows系统中同样很常见,比如Windows Media Player中控制按钮等等。
非规则窗体的制作关键是要得到一个表示这个不规则区域的数据结构,MFC里由CRgn类来表示。要通过图片来获得按钮区域,只需要检测相关的点生成这个类的实例即可。
GDI+功能强大,处理图片更是轻松多了,直接支持RGBA色彩。
如下所示,直接检测象素点的A分量是否为0,不为0则该点就是窗体区域的一部分。
- Region* CSkinButton::CreateRegionFrom2(Image* img)
- {
- Bitmap* bmp = new Bitmap(img->GetWidth(), img->GetHeight());
- Graphics* g = Graphics::FromImage(bmp);
- g->DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());
- GraphicsPath gp;
- for ( int i = 0; i < ( int )bmp->GetWidth(); i ++)
- {
- for ( int j = 0; j < ( int )bmp->GetHeight(); j++)
- {
- Color cl;
- bmp->GetPixel(i, j, &cl);
- if (cl.GetA() != 0)
- {
- gp.AddRectangle(Rect(i, j, 1, 1));
- }
- }
- }
- delete g;
- delete bmp;
- return new Region(&gp);
- }
完整代码如下所示,CSkinButton类可以直接根据图象,生成自身的窗体区域。
- /**
- * @brief 皮肤按钮
- * 皮肤按钮实现了跟椐图片的透明区,来实现不规则按钮
- * @note 此类只支持png格式图片的透明区检测,透明区的Alpha分量必须为0才被切掉
- */
- class CSkinButton : public CButton
- {
- DECLARE_DYNAMIC(CSkinButton)
- public :
- /** 构造器 */
- CSkinButton();
- /** 析构函数 */
- virtual ~CSkinButton();
- /** 设置按钮图片 */
- virtual void SetImage(Image* img);
- virtual void Create(CWnd* pParentWnd, UINT nID);
- private :
- /** 设置窗体区域
- * @param region 窗体区域
- */
- void SetRgn(CRgn *region);
- /** 读取图片的非透明区生成区域
- * @param img 图片对象
- */
- Region* CreateRegionFrom2(Image* img);
- Image* m_image;
- Point m_imgOffset;
- HRGN rgn, trgn;
- protected :
- DECLARE_MESSAGE_MAP()
- virtual void PreSubclassWindow();
- public :
- virtual void DrawItem(LPDRAWITEMSTRUCT /*lpDrawItemStruct*/ );
- afx_msg void OnLButtonDown( UINT nFlags, CPoint point);
- afx_msg void OnLButtonUp( UINT nFlags, CPoint point);
- };
- #include "stdafx.h"
- #include "Widget Simulator.h"
- #include "SkinButton.h"
- #include <atlimage.h>
- // CSkinButton
- IMPLEMENT_DYNAMIC(CSkinButton, CButton)
- CSkinButton::CSkinButton()
- {
- this ->m_image = NULL;
- this ->m_imgOffset = Point(0, 0);
- }
- void CSkinButton::SetImage(Image* img)
- {
- this ->m_image = img;
- Region* rgn = CreateRegionFrom2(img);
- Graphics g(GetWindowDC()->m_hDC);
- HRGN hrgn = rgn->GetHRGN(&g);
- CRgn * crgn = CRgn::FromHandle(hrgn);
- SetRgn(crgn);
- delete rgn;
- //delete crgn;
- }
- CSkinButton::~CSkinButton()
- {
- if (m_image)
- {
- delete m_image;
- }
- }
- void CSkinButton::Create(CWnd* pParentWnd, UINT nID)
- {
- CWnd* target = pParentWnd->GetDlgItem(nID);
- CRect rc;
- target->GetWindowRect(&rc);
- pParentWnd->ScreenToClient(&rc);
- __super::Create(NULL, WS_CHILD | WS_VISIBLE |WS_TABSTOP|BS_PUSHBUTTON | BS_OWNERDRAW, CRect(rc.left,
- rc.top,
- m_image->GetWidth() + rc.left,
- m_image->GetHeight() + rc.top),
- pParentWnd,
- nID);
- }
- Region* CSkinButton::CreateRegionFrom2(Image* img)
- {
- Bitmap* bmp = new Bitmap(img->GetWidth(), img->GetHeight());
- Graphics* g = Graphics::FromImage(bmp);
- g->DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());
- GraphicsPath gp;
- for ( int i = 0; i < ( int )bmp->GetWidth(); i ++)
- {
- for ( int j = 0; j < ( int )bmp->GetHeight(); j++)
- {
- Color cl;
- bmp->GetPixel(i, j, &cl);
- if (cl.GetA() != 0)
- {
- gp.AddRectangle(Rect(i, j, 1, 1));
- }
- }
- }
- delete g;
- delete bmp;
- return new Region(&gp);
- }
- BEGIN_MESSAGE_MAP(CSkinButton, CButton)
- ON_WM_LBUTTONDOWN()
- ON_WM_LBUTTONUP()
- END_MESSAGE_MAP()
- void CSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
- {
- SetWindowRgn(rgn, true );
- if (m_image)
- {
- Graphics g(lpDrawItemStruct->hDC);
- g.SetClip(trgn, CombineModeReplace);
- switch (lpDrawItemStruct->itemAction)
- {
- case ODA_SELECT:
- case ODA_DRAWENTIRE:
- case ODA_FOCUS:
- {
- g.DrawImage(m_image, RectF((REAL)m_imgOffset.X, (REAL)m_imgOffset.Y, (REAL)m_image->GetWidth(), (REAL)m_image->GetHeight()));
- }break ;
- }
- }
- }
- void CSkinButton::SetRgn(CRgn *region)
- {
- rgn = ::CreateRectRgn(0, 0, 0, 0);
- CombineRgn(rgn, region->operator HRGN (), NULL, RGN_COPY);
- trgn = ::CreateRectRgn(0, 0, 0, 0);
- CombineRgn(trgn, region->operator HRGN (), NULL, RGN_COPY);
- }
- void CSkinButton::PreSubclassWindow()
- {
- ModifyStyle(0, BS_OWNERDRAW | WS_CLIPSIBLINGS);
- CButton::PreSubclassWindow();
- }
- void CSkinButton::OnLButtonDown( UINT nFlags, CPoint point)
- {
- m_imgOffset.X = 1;
- m_imgOffset.Y = 1;
- CButton::OnLButtonDown(nFlags, point);
- }
- void CSkinButton::OnLButtonUp( UINT nFlags, CPoint point)
- {
- m_imgOffset.X = 0;
- m_imgOffset.Y = 0;
- CButton::OnLButtonUp(nFlags, point);
- }
这是用这个类做的一个实例:左边是用photoshop做设计,右边是运行效果。