利用位图创建异形对话框原理是根据象素的颜色来进行“扣像”处理,对所有非指定颜色象素区域进行区域组合。利用这一技术,实际上就是实现对话框/窗口的位图背景,并且对指定的颜色区域进行透明处理。下面就以透明位图为背景的对话框为例来说明:
首先用绘图软件如PhotoShop绘制编辑一幅拟做对话框背景用的图片,用BMP格式保存,假设存为Back.Bmp。需要说明的是,虽然Visual C++集成开发环境的资源编辑器只能编辑不超过16色的位图,但完全我们可以以真彩色方式存储,不必理会Visual C++的警告。
下一步是用Visual C++的AppWizard创建一个基于对话框的应用程序假定命名为Trans。用资源编辑器引入背景图片Back.Bmp,如果是高彩色,不必理会出现的警告信息,点击OK确认即可。为了明确,修改默认的资源ID标识IDB_BITMAP1为IDB_BACKBMP。然后修改对话框的Style为Popup,Border为None,如图1。
图1
向CTransDlg类添加区域处理功能模块void CTransDlg::SetupRegion(CDC *pDC /*对话框窗口DC*/, UINT BackBitmapID /*背景位图资源ID*/, UINT MaskBitmapID /*区域处理位图资源ID*/, COLORREF TransColor = 0x00000000 /*透明颜色值,默认为黑色*/)。到目前为止,我们暂时认为MaskBitmapID等同于BackBitmapID。其核心工作是根据MaskBitmapID指示位图的象素颜色进行区域组合。完整的代码如下:
void CTransDlg::SetupRegion(CDC *pDC /*对话框窗口DC*/,
UINT BackBitmapID /*背景位图资源ID*/,
UINT MaskBitmapID /*区域处理位图资源ID*/,
COLORREF TransColor /*透明颜色值*/)
{
CDC memDC;
CBitmap cBitmap;
CBitmap* pOldMemBmp = NULL;
COLORREF cl;
CRect cRect;
UINT x, y;
CRgn wndRgn, rgnTemp;
//取得窗口大小
GetWindowRect(&cRect);
//背景位图资源ID
m_BackBitmapID = BackBitmapID
//装载位图
cBitmap.LoadBitmap(MaskBitmapID);
memDC.CreateCompatibleDC(pDC);
pOldMemBmp = memDC.SelectObject(&cBitmap);
//首先创建默认的完整区域为完整的窗口区域
wndRgn.CreateRectRgn(0, 0, cRect.Width(), cRect.Height());
//下面的两层循环为检查背景位图象素颜色,进行透明区域处理;
//当象素颜色为指定的透明值时,即将该点从区域中剪裁掉。
//其中用到的几个成员变量m_MaskLeftOff、m_MaskTopOff、
//m_MaskRightOff、m_MaskBottomOff、m_FrameWidth
//和m_CaptionHeight,其作用后面再作说明,此时可全部当作0来处理。
for(x= m_FrameWidth+m_MaskLeftOff;
x<=cRect.Width() - m_FrameWidth-m_MaskRightOff; x++){
for(y = m_CaptionHeight+m_MaskTopOff;
y<=cRect.Height() - m_FrameWidth-m_MaskBottomOff; y++){
//取得坐标处象素的颜色值
cl = memDC.GetPixel(x - m_FrameWidth-m_MaskLeftOff,
y - m_CaptionHeight-m_MaskTopOff);
if(col == TransColor)
{
//象素颜色为指定的透明色,创建透明“微区域”
rgnTemp.CreateRectRgn(x, y, x+1, y+1);
//“扣像”,从完整的区域中“扣除”透明的“微区域”
wndRgn.CombineRgn(&wndRgn, &rgnTemp, RGN_XOR);
//删除刚创建的透明“微区域”,释放系统资源
rgnTemp.DeleteObject();
}
}
}
if (pOldMemBmp) memDC.SelectObject(pOldMemBmp);
//用设定窗口为指定的区域
SetWindowRgn((HRGN)wndRgn, TRUE);
}
重置系统默认的背景擦除操作,即添加WM_ERASEBKGND消息处理过程,这一步可以借助ClassWizard来简化操作。
BOOL CTransDlg::OnEraseBkgnd(CDC* pDC)
{
// TODO: Add your message handler code here and/or call default
CRect rect;
CDC memDC;
CBitmap cBitmap;
CBitmap* pOldMemBmp = NULL;
GetWindowRect(&rect);
//装载背景位图
cBitmap.LoadBitmap(m_BackBitmapID);
memDC.CreateCompatibleDC(pDC);
pOldMemBmp = memDC.SelectObject(&cBitmap);
//将背景位图复制到窗口客户区
pDC->BitBlt(0, 0, rect.Width(), rect.Height(),
&memDC, 0, 0, SRCCOPY);
if (pOldMemBmp) memDC.SelectObject( pOldMemBmp );
//删除系统却省的OnEraseBkgnd功能
//return CDialog::OnEraseBkgnd(pDC);
return TRUE;
}
接下来是在WM_PAINT的消息处理函数OnPaint()中添加代码。由于当背景位图比较大时,进行区域处理比较耗时,所以只在启动时进行一次处理。一种方法是OnInitDialog()处理,但这样会在从启动程序到窗口出现有相当的延迟,易引起程序尚未启动的误解。再一种方法就是在OnPaint()处理,但为了避免重复处理,可以加上一个判断标志。以下是OnPaint()的代码,正体为AppWizard生成,粗体为自己添加内容。
void CTransDlg::OnPaint()
{
if (IsIconic())
{
……
}
else
{
if(m_nFirstRun){ //首次运行标志
//修改鼠标光标为等待方式
BeginWaitCursor();
//设置背景区域
SetupRegion(GetWindowDC(),
IDB_BACKBMP,
IDB_BACKBMP,
0x00FFFFFF /*白色*/);
//恢复鼠标光标为正常模式
EndWaitCursor();
m_nFirstRun = 0;
}
CDialog::OnPaint();
}
}
剩下的工作就是根据背景位图的大小来设置对话框窗口的大小和位置,这可以在OnInitDialog()中通过调用MoveWindow()来实现。再添加一些变量的声名和初始化,即可编译运行。图2为运行结果示例:
图2
利用位图创建异形对话框窗口
最新推荐文章于 2023-10-18 15:27:20 发布