通过检测png图片透明区实现不规则按钮

本文介绍了一种使用图片透明区域来实现非矩形按钮的方法,通过GDI+库读取图片像素并创建相应的窗体区域。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  来源:http://blog.youkuaiyun.com/enin_dqc/archive/2010/09/09/5874327.aspx

     常见的按钮或窗体都是四四方方的矩形,默认创建产生的window窗体都是矩形的。但非矩形窗体在Windows系统中同样很常见,比如Windows Media Player中控制按钮等等。

    非规则窗体的制作关键是要得到一个表示这个不规则区域的数据结构,MFC里由CRgn类来表示。要通过图片来获得按钮区域,只需要检测相关的点生成这个类的实例即可。

    GDI+功能强大,处理图片更是轻松多了,直接支持RGBA色彩。

    如下所示,直接检测象素点的A分量是否为0,不为0则该点就是窗体区域的一部分。

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. Region* CSkinButton::CreateRegionFrom2(Image* img)  
  2. {   
  3.     Bitmap* bmp = new  Bitmap(img->GetWidth(), img->GetHeight());  
  4.     Graphics* g = Graphics::FromImage(bmp);  
  5.     g->DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());  
  6.    
  7.     GraphicsPath gp;  
  8.     for  ( int  i = 0; i < ( int )bmp->GetWidth(); i ++)  
  9.     {  
  10.         for  ( int  j = 0; j < ( int )bmp->GetHeight(); j++)  
  11.         {  
  12.             Color cl;  
  13.             bmp->GetPixel(i, j, &cl);  
  14.             if  (cl.GetA() != 0)  
  15.             {  
  16.                 gp.AddRectangle(Rect(i, j, 1, 1));  
  17.             }  
  18.         }  
  19.     }  
  20.     delete  g;  
  21.     delete  bmp;  
  22.     return   new  Region(&gp);  
  23. }  

 

完整代码如下所示,CSkinButton类可以直接根据图象,生成自身的窗体区域。

·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. /**  
  2.  *  @brief 皮肤按钮  
  3.  * 皮肤按钮实现了跟椐图片的透明区,来实现不规则按钮  
  4.  * @note 此类只支持png格式图片的透明区检测,透明区的Alpha分量必须为0才被切掉  
  5.  */   
  6. class  CSkinButton :  public  CButton  
  7. {  
  8.     DECLARE_DYNAMIC(CSkinButton)  
  9. public :  
  10.     /** 构造器 */   
  11.     CSkinButton();  
  12.     /** 析构函数 */   
  13.     virtual  ~CSkinButton();  
  14.     /** 设置按钮图片 */   
  15.     virtual   void  SetImage(Image* img);  
  16.     virtual   void  Create(CWnd* pParentWnd,  UINT  nID);  
  17. private :  
  18.     /** 设置窗体区域  
  19.     * @param region 窗体区域  
  20.     */   
  21.     void  SetRgn(CRgn *region);  
  22.     /** 读取图片的非透明区生成区域   
  23.     * @param img 图片对象  
  24.     */   
  25.     Region* CreateRegionFrom2(Image* img);  
  26.     Image* m_image;  
  27.     Point m_imgOffset;  
  28.     HRGN  rgn, trgn;  
  29. protected :  
  30.     DECLARE_MESSAGE_MAP()  
  31.     virtual   void  PreSubclassWindow();  
  32. public :    
  33.     virtual   void  DrawItem(LPDRAWITEMSTRUCT  /*lpDrawItemStruct*/ );   
  34.     afx_msg void  OnLButtonDown( UINT  nFlags, CPoint point);  
  35.     afx_msg void  OnLButtonUp( UINT  nFlags, CPoint point);  
  36. };  

 
·········10········20········30········40········50········60········70········80········90········100·······110·······120·······130·······140·······150
  1. #include "stdafx.h"   
  2. #include "Widget Simulator.h"   
  3. #include "SkinButton.h"   
  4. #include <atlimage.h>    
  5. // CSkinButton   
  6. IMPLEMENT_DYNAMIC(CSkinButton, CButton)  
  7. CSkinButton::CSkinButton()  
  8. {  
  9.     this ->m_image = NULL;  
  10.     this ->m_imgOffset = Point(0, 0);  
  11. }  
  12. void  CSkinButton::SetImage(Image* img)  
  13. {   
  14.     this ->m_image = img;  
  15.     Region* rgn = CreateRegionFrom2(img);  
  16.     Graphics g(GetWindowDC()->m_hDC);  
  17.     HRGN  hrgn = rgn->GetHRGN(&g);  
  18.     CRgn * crgn = CRgn::FromHandle(hrgn);  
  19.     SetRgn(crgn);  
  20.     delete  rgn;  
  21.     //delete crgn;   
  22. }  
  23. CSkinButton::~CSkinButton()  
  24. {  
  25.     if  (m_image)  
  26.     {  
  27.         delete  m_image;  
  28.     }  
  29. }  
  30. void  CSkinButton::Create(CWnd* pParentWnd,  UINT  nID)  
  31. {  
  32.     CWnd* target = pParentWnd->GetDlgItem(nID);  
  33.     CRect rc;  
  34.     target->GetWindowRect(&rc);  
  35.     pParentWnd->ScreenToClient(&rc);  
  36.     __super::Create(NULL, WS_CHILD | WS_VISIBLE |WS_TABSTOP|BS_PUSHBUTTON | BS_OWNERDRAW, CRect(rc.left,   
  37.         rc.top,   
  38.         m_image->GetWidth() + rc.left,   
  39.         m_image->GetHeight() + rc.top),  
  40.         pParentWnd,   
  41.         nID);   
  42. }  
  43. Region* CSkinButton::CreateRegionFrom2(Image* img)  
  44. {   
  45.     Bitmap* bmp = new  Bitmap(img->GetWidth(), img->GetHeight());  
  46.     Graphics* g = Graphics::FromImage(bmp);  
  47.     g->DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());  
  48.    
  49.     GraphicsPath gp;  
  50.     for  ( int  i = 0; i < ( int )bmp->GetWidth(); i ++)  
  51.     {  
  52.         for  ( int  j = 0; j < ( int )bmp->GetHeight(); j++)  
  53.         {  
  54.             Color cl;  
  55.             bmp->GetPixel(i, j, &cl);  
  56.             if  (cl.GetA() != 0)  
  57.             {  
  58.                 gp.AddRectangle(Rect(i, j, 1, 1));  
  59.             }  
  60.         }  
  61.     }  
  62.     delete  g;  
  63.     delete  bmp;  
  64.     return   new  Region(&gp);  
  65. }  
  66. BEGIN_MESSAGE_MAP(CSkinButton, CButton)    
  67.     ON_WM_LBUTTONDOWN()  
  68.     ON_WM_LBUTTONUP()  
  69. END_MESSAGE_MAP()  
  70.     
  71. void  CSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)  
  72. {     
  73.     SetWindowRgn(rgn, true );  
  74.     if  (m_image)  
  75.     {  
  76.         Graphics g(lpDrawItemStruct->hDC);  
  77.         g.SetClip(trgn, CombineModeReplace);  
  78.         switch (lpDrawItemStruct->itemAction)   
  79.         {  
  80.         case  ODA_SELECT:  
  81.         case  ODA_DRAWENTIRE:  
  82.         case  ODA_FOCUS:  
  83.             {  
  84.                 g.DrawImage(m_image, RectF((REAL)m_imgOffset.X, (REAL)m_imgOffset.Y, (REAL)m_image->GetWidth(), (REAL)m_image->GetHeight()));  
  85.             }break ;   
  86.         }          
  87.     }   
  88. }  
  89. void  CSkinButton::SetRgn(CRgn *region)  
  90. {  
  91.     rgn = ::CreateRectRgn(0, 0, 0, 0);   
  92.     CombineRgn(rgn, region->operator HRGN (), NULL, RGN_COPY);  
  93.     trgn = ::CreateRectRgn(0, 0, 0, 0);  
  94.     CombineRgn(trgn, region->operator HRGN (), NULL, RGN_COPY);  
  95. }  
  96. void  CSkinButton::PreSubclassWindow()  
  97. {  
  98.     ModifyStyle(0, BS_OWNERDRAW | WS_CLIPSIBLINGS);   
  99.     CButton::PreSubclassWindow();  
  100. }  
  101.    
  102.  void  CSkinButton::OnLButtonDown( UINT  nFlags, CPoint point)  
  103.  {  
  104.      m_imgOffset.X = 1;  
  105.      m_imgOffset.Y = 1;  
  106.      CButton::OnLButtonDown(nFlags, point);  
  107.  }  
  108.  void  CSkinButton::OnLButtonUp( UINT  nFlags, CPoint point)  
  109.  {  
  110.      m_imgOffset.X = 0;  
  111.      m_imgOffset.Y = 0;  
  112.      CButton::OnLButtonUp(nFlags, point);  
  113.  }  

 

这是用这个类做的一个实例:左边是用photoshop做设计,右边是运行效果。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值