MFC贴图

怎样去除图片上的背景颜色实现透明贴图?

查了一些资料并参考一些帖子总结了一下有几种方法

由简单到复杂:

方法一:

使用TransparentBlt;

void CGdiDlg::OnPaint()
{
    
if (IsIconic())
    
{
         CPaintDC dc(
this); // 用于绘制的设备上下文

         SendMessage(WM_ICONERASEBKGND, reinterpret_cast
<WPARAM>(dc.GetSafeHdc()), 0);

        
// 使图标在工作矩形中居中
        int cxIcon = GetSystemMetrics(SM_CXICON);
        
int cyIcon = GetSystemMetrics(SM_CYICON);
         CRect rect;
         GetClientRect(
&rect);
        
int x = (rect.Width() - cxIcon + 1) / 2;
        
int y = (rect.Height() - cyIcon + 1) / 2;

        
// 绘制图标
         dc.DrawIcon(x, y, m_hIcon);
     }

    
else
    
{
         CPaintDC dc(
this);
         CDC memDc;
         memDc.CreateCompatibleDC(
&dc);
         BITMAP bm;
         m_bitmap.GetBitmap(
&bm);
         memDc.SelectObject(
&m_bitmap);
                          dc.TransparentBlt(
10, 10, bm.bmWidth, bm.bmHeight, &memDc, 0, 0, bm.bmWidth, bm.bmHeight, RGB(255, 255, 255));
                      }

}

        

方法二:

使用MaskBlt;

{
     ......        
     CPaintDC dc(
this);
     CDC memDc;
     memDc.CreateCompatibleDC(
&dc);
    
//CBitmap memBitmap;
     BITMAP bm;
     m_bitmap.GetBitmap(
&bm);
     memDc.SelectObject(
&m_bitmap);

     CDC dcmask;
     dcmask.CreateCompatibleDC(
&dc);
     CBitmap bmpmask;
     bmpmask.CreateBitmap(bm.bmWidth, bm.bmHeight,
1, 1, NULL);
     dcmask.SelectObject(
&bmpmask);
    
     dcmask.FillSolidRect(
0, 0, bm.bmWidth, bm.bmHeight, RGB(255, 255, 255));
     dc.MaskBlt(
0, 0, bm.bmWidth, bm.bmHeight, &memDc, 0, 0, bmpmask, 0, 0, MAKEROP4(SRCAND, SRCINVERT));
     ......
}

方法三:

使用BitBlt的光栅操作

{
     .....
     CPaintDC dc(
this);
     CDC memDc;
     memDc.CreateCompatibleDC(
&dc);
     BITMAP bm;
     m_bitmap.GetBitmap(
&bm);
     memDc.SelectObject(
&m_bitmap);

     CDC dcmask;
     dcmask.CreateCompatibleDC(
&dc);
     CBitmap bmpmask;
     bmpmask.CreateBitmap(bm.bmWidth, bm.bmHeight,
1, 1, NULL);
     dcmask.SelectObject(
&bmpmask);
     memDc.SetBkColor(RGB(
255, 255, 255));
     dcmask.BitBlt(
0, 0, bm.bmWidth, bm.bmHeight, &memDc, 0, 0, SRCCOPY);

     dc.BitBlt(
0, 0, bm.bmWidth, bm.bmHeight, &memDc, 0, 0, SRCINVERT);

     dc.BitBlt(
0, 0, bm.bmWidth, bm.bmHeight, &dcmask, 0, 0, SRCAND);
     dc.BitBlt(
0, 0, bm.bmWidth, bm.bmHeight, &memDc, 0, 0, SRCINVERT);
     .....
}

上述代码的原理,直观的说,目标就是,把memDc绘制到dc上的时候,
不绘制跟背景相同的颜色的部分。
1.用BitBlt API进行透明显示的步骤:
①处理dcmask为黑白DC,使memDc上颜色为背景的部分在dcmask显示为白色,其余地方显示为黑色。
②将memDc用BitBlt绘制到dc上,使用SRCINVERT方式
③将dcmask用BitBlt绘制到dc上,使用SRCAND方式
④再将memDc用BitBlt绘制到dc上,使用SRCINVERT方式

ROP中,SRCINVERT是图像间异或处理,SRCAND是图像间与处理。可以简单证明上述的
操作过程会得到我们想要的结果:

对于某一个位置,dc上颜色为B,memDc上颜色为A。
当A == 背景色的时候,dcmask上这个位置的颜色M为白色。则上面的②~④步可以表示为:
((B xor A) and M) xor A
⇔ (B xor A) xor A
⇔ B
当A != 背景色的时候,dcmask上这个位置的颜色M为黑色。则上面的②~④步可以表示为:
((B xor A) and M) xor A
⇔ 0 xor A
⇔ A

前两种方法TransparentBlt和MaskBlt API windows 95/98/me不支持,只支持NT/2000/XP及以后版本




在CDialog类中进行贴图,一般放在OnPaint()函数中,因为窗口更新时,使用它来进行重绘。在OnPain()中贴图的源码如下:

void C***Dialog::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 
 // TODO: Add your message handler code here
 // CPaintDC  dc(this); 
 CRect  rect; 
 GetClientRect(&rect); 
 CDC  dcMem; 
 dcMem.CreateCompatibleDC(&dc);///建立关联DC
 CBitmap  bmpBackground; 
 bmpBackground.LoadBitmap(IDB_BITMAP_BKK);   //IDB_BITMAP是你自己的图对应的ID 
 BITMAP  bitmap; 
 bmpBackground.GetBitmap(&bitmap); 
 CBitmap  *pbmpOld=dcMem.SelectObject(&bmpBackground); 
 dc.StretchBlt(0,0,rect.Width(),rect.Height(),&dcMem,0,0, 
  bitmap.bmWidth,bitmap.bmHeight,SRCCOPY);
 // Do not call CDialog::OnPaint() for painting messages
}

当你贴图完毕后,会发现很多地方出现了原来的底色,这是因为这是你的其他东东即控件,的画刷没有设置透明背景色。所有下一步就是设置透明背景色,代码如下:

HBRUSH C***Dialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
 HBRUSH hbr = CGraphDialog::OnCtlColor(pDC, pWnd, nCtlColor);
 
 // TODO: Change any attributes of the DC here
 if( nCtlColor == CTLCOLOR_STATIC)     
 {   
  pDC->SetBkMode(TRANSPARENT);   //设置背景透明
  return   HBRUSH(GetStockObject(HOLLOW_BRUSH));
 }
 // TODO: Return a different brush if the default is not desired
 return hbr;
}

 

这两个函数均为消息响应函数,所以还需要添加消息响应

.H文件中

protected:

 // Generated message map functions
 //{{AFX_MSG(CCreateArcBy2Point)
  afx_msg void OnPaint();
 afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()

.CPP文件中

BEGIN_MESSAGE_MAP(CAllMaterialDlg, CDialog)
 //{{AFX_MSG_MAP(CAllMaterialDlg)
 ON_WM_PAINT()
 ON_WM_CTLCOLOR()
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

 

这个时候贴图的效果就非常好了,但是你如果动态的修改静态文本框控件内容时候,又发现了新的问题,发现字符可能重叠。这个原因是你在OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 函数中,将画刷设置为透明引起的。那怎么解决内,你就需要,在OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) 函数中返回一个具有背景的画刷了。具体的办法可以自己去琢磨,我这里有个比较笨的办法,给大家提供一个借鉴:

HBRUSH CAllMaterialDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
 HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
 
 // TODO: Change any attributes of the DC here
 if( nCtlColor == CTLCOLOR_STATIC)     
 {   
  pDC->SetBkMode(TRANSPARENT);   //设置背景透明
//  return   HBRUSH(GetStockObject(HOLLOW_BRUSH));
  hbr=(HBRUSH)(m_brush.GetSafeHandle());  
 }
 // TODO: Return a different brush if the default is not desired
 return hbr;
}



IPicture *m_picture;
OLE_XSIZE_HIMETRIC m_width;
OLE_YSIZE_HIMETRIC m_height;

CString m_filename("G:\\my photo\\照片和视频\\DSC_00516.jpg");//文件名

CFile m_file(m_filename,CFile::modeRead );

//获取文件长度
DWORD m_filelen = m_file.GetLength();

//在堆上分配空间
HGLOBAL m_hglobal = GlobalAlloc(GMEM_MOVEABLE,m_filelen);

LPVOID pvdata = NULL;
//锁定堆空间,获取指向堆空间的指针
pvdata = GlobalLock(m_hglobal);

//将文件数据读区到堆中
m_file.Read(pvdata,m_filelen);

IStream* m_stream;

GlobalUnlock(m_hglobal);

//在堆中创建流对象
CreateStreamOnHGlobal(m_hglobal,TRUE,&m_stream);

//利用流加载图像
OleLoadPicture(m_stream,m_filelen,TRUE,IID_IPicture,(LPVOID*)&m_picture);

m_picture->get_Width(&m_width);
m_picture->get_Height(&m_height);   

CDC* dc = GetDC();

//m_IsShow = TRUE;
CRect rect;
GetClientRect(rect);
SetScrollRange(SB_VERT,0,(int)(m_height/26.45)-rect.Height());
SetScrollRange(SB_HORZ,0,(int)(m_width/26.45)-rect.Width());

m_picture->Render(*dc,1,50,(int)(m_width/26.45),(int)(m_height/26.45),0,m_height,m_width,-m_height,NULL);



你可能感兴趣的:(MFC贴图)