专题:DUILIB Win32 透明效果

时间:2021-10-11 07:56:06

Layered Windows 分层窗口。这是Windows2000开始引入的概念,重新定义了窗口的Hit Testing方法,以前都是把窗口按rectangle的方式裁剪,而把窗口加上WS_EX_LAYERED的Style后就可以根据窗口的形状和像素 值进行Hit Testing,这样我们的不规则窗口就变成了真正意义上的独立窗口,而不是传统的被一个不可见的矩形窗口所包含。

分层窗口重绘方式、透明效果产生

创建不规则窗口的三种方式
1. 通过区域相关API设置窗口的区域SetWindowRgn;;
2. 通过 SetLayeredWindowAttributes来指定特殊的透明颜色,让背景图的部分位置全透从而实现窗口的“不规则”;
3. 通过 UpdateLayeredWindow来指定特殊颜色透明或者根据图片的ALPHA值来设置窗口全透。

UpdateLayeredWindow():直接更新一个分层的窗口的位置,大小,形状,内容和半透明度。优点是一劳永逸,不需要在窗口函数中响应各种重绘事件。

先用SetLayeredWindowAttributes()函数设置关于窗口透明度的信息,然后用传统方式,在窗口函数中响应各种重绘事件。

注:分层窗口上的控件会随着窗口一同变透明
解决方案:准备两个窗口,窗口A和窗口B,窗口A作为显示窗口,也就是异形窗口,而窗口B作为逻辑窗口,然后让这两个窗口 重叠在一块,,也可以说是在窗口B上创建了窗口A,然后通过UpdateLayeredWindow对窗口A实现异形,因为窗口A在窗口B上,那么势必会遮 盖住窗口B的控件,然后我们就要对窗口A通过SetWindowRgn进行裁剪,通过镂空出控件的位置从而达到显示出控件。

不规则区域重绘

SetWindowRgn() 函数:用于设置了一个窗口的区域.只有被包含在这个区域内的地方才会被重绘,而不包含在区域内的其他区域系统将不会显示.

我们的不规则形状由此而来。这个函数和它的朋友们十分强大,不仅可以定义独立的基本形状的区 域,还可以通过运算(CombineRgn:可对两个区域进行交集、拷贝、相减、异或运算)来组合已有区域从而产生新的区域。

异形窗口效果示例

简单效果

复杂效果

DUILIB库 对透明效果的实现

DUILIB中,窗体透明度设置方法
1.设置window标签属性bktrans="true" alpha="200" alpha的值为0-255。这种设置是全体窗体透明度,所有控件都将变透明。
2.如果想单纯设置背景透明度控件不透明度,可以制作半透明的背景图片,设置window标签的bktrans="true",并且不设置alpha属性,此时背景透明,其它控件不透明。
3.单独设置某个控件的透明度,可以使用图片的fade属性,或者mask属性。fade表示设置图片透明度,取值0-255。mask为设置透明的颜色。

DuiLib.CRenderEngine.LoadImage 对控件指定颜色mask的透明:在加载图片资源位图时,对颜色为mask的像素进行置零,绘制时这些像素的位置将被透明 if( *(DWORD*)(&pDest[i*4]) == mask ) { pDest[i*4] = (BYTE)0; pDest[i*4 + 1] = (BYTE)0; pDest[i*4 + 2] = (BYTE)0; pDest[i*4 + 3] = (BYTE)0; bAlphaChannel = true; } 图片资源文件只加载一次,加载成功后缓存于内存中。 // data : 加载的图片资源 if( !data ) return NULL; if( type != NULL ) data->sResType = type; data->dwMask = mask; if( !m_mImageHash.Insert(bitmap, data) ) {// 缓存提速 ::DeleteObject(data->hBitmap); delete data; data = NULL; } DuiLib.CRenderEngine.DrawImage 渲染引擎:透明效果渲染过程 static LPALPHABLEND lpAlphaBlend = (LPALPHABLEND) ::GetProcAddress(::GetModuleHandle(_T("msimg32.dll")), "AlphaBlend"); .... .... if( lpAlphaBlend && (alphaChannel || uFade < 255) ) { // uFade:设置背景图片透明度; alphaChannel:启用指定颜色(Mask)透明 BLENDFUNCTION bf = { AC_SRC_OVER, 0, uFade, AC_SRC_ALPHA }; if( lpAlphaBlend == NULL ) lpAlphaBlend = AlphaBitBlt; .... .... // Alpha混合贴图 lpAlphaBlend(hDC, lDestLeft, rcDest.top, lDestRight - lDestLeft, rcDest.bottom, hCloneDC, rcBmpPart.left + rcCorners.left, rcBmpPart.top + rcCorners.top, lDrawWidth, rcBmpPart.bottom - rcBmpPart.top - rcCorners.top - rcCorners.bottom, bf); DuiLib.CControlUI.DoPaint 控件重绘过程 void CControlUI::DoPaint(HDC hDC, const RECT& rcPaint) { if( !::IntersectRect(&m_rcPaint, &rcPaint, &m_rcItem) ) return; // 绘制循序:背景颜色->背景图->状态图->文本->边框 if( m_cxyBorderRound.cx > 0 || m_cxyBorderRound.cy > 0 ) { CRenderClip roundClip; // CRenderClip内部以SetWindowRgn()函数对绘制区域进行控制 CRenderClip::GenerateRoundClip(hDC, m_rcPaint, m_rcItem, m_cxyBorderRound.cx, m_cxyBorderRound.cy, roundClip); PaintBkColor(hDC); PaintBkImage(hDC);// 调用渲染引擎绘制背景图片 PaintStatusImage(hDC); PaintText(hDC); PaintBorder(hDC); } else { PaintBkColor(hDC); PaintBkImage(hDC); PaintStatusImage(hDC); PaintText(hDC); PaintBorder(hDC); } } DuiLib.CPaintManagerUI.MessageHandler 各控件绘制完成后,整体粘贴到主窗口进行显示 BLENDFUNCTION blendPixelFunction = {AC_SRC_OVER, 0, m_nOpacity, AC_SRC_ALPHA}; BOOL bRet = ::UpdateLayeredWindow(m_hWndPaint, NULL, &pt, &szWindow, m_hDcOffscreen, &ptSrc, 0, &blendPixelFunction, ULW_ALPHA); 备课神器中,封面截图过程中遮罩效果的实现 目标效果

A为用户鼠标选取部分,无遮罩;
B为未选取部分,全部阴影遮罩(示例图未完善,遮罩未覆盖整个图片)