怎样去除图片上的背景颜色实现透明贴图?
查了一些资料并参考一些帖子总结了一下有几种方法
由简单到复杂:
方法一:
使用TransparentBlt;































方法二:
使用MaskBlt;




















方法三:
使用BitBlt的光栅操作























上述代码的原理,直观的说,目标就是,把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;
}
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);