[随笔]关于如何实现鼠标穿透窗口和窗口半透明

资料准备

  • WS_EX_TRANSPARENT | WS_EX_LAYERED
  • WM_NCHITTEST & return HTTRANSPARENT
  • SetLayeredWindowAttributes(…)
  • UpdateLayeredWindow(…)

旁征博引

窗口类型

  • Overlapped Windows
  • Pop-up Windows
  • Child Windows
  • Layered Windows             \\分层窗口(这是我们这节课研究的重点)
  • Message-Only Windows     \\消息窗口

分层窗口详解:

 

Using a layered window can significantly improve performance and visual effects for a window that has a complex shape, animates its shape, or wishes to use alpha blending effects. The system automatically composes and repaints layered windows and the windows of underlying applications. As a result, layered windows are rendered smoothly, without the flickering typical of complex window regions. In addition, layered windows can be partially translucent, that is, alpha-blended.

这段大概就是说,layered窗口能够改善显示效果,给你的窗体加个alpha通道,可以平滑半透明地显示。嗯,还可以设置为一部分透明一部分不透明的样子。(据我研究,应该还能设置成渐变透明。)

To create a layered window, specify the WS_EX_LAYERED extended window style when calling the CreateWindowEx function, or call the SetWindowLong function to set WS_EX_LAYERED after the window has been created. After the CreateWindowEx call, the layered window will not become visible until the SetLayeredWindowAttributes or UpdateLayeredWindow function has been called for this window. Note that WS_EX_LAYERED cannot be used for child windows.

一旦你指定了这个属性,刚开始这个窗体是不能显示的,你得先设置整个窗体的Alpha通道。通过这两个函数SetLayeredWindowAttributes、UpdateLayeredWindow

值得一提的的是这个不能用于子窗体(也就是不能和WS_CHILD共存呗)。

To set the opacity level or the transparency color key for a given layered window, call SetLayeredWindowAttributes. After the call, the system may still ask the window to paint when the window is shown or resized. However, because the system stores the image of a layered window, the system will not ask the window to paint if parts of it are revealed as a result of relative window moves on the desktop. Legacy applications do not need to restructure their painting code if they want to add translucency or transparency effects for a window, because the system redirects the painting of windows that called SetLayeredWindowAttributes into off-screen memory and recomposes it to achieve the desired effect.

嗯,这段说实话以我高一的英语水平也没看太懂。大致意思就是,系统可能仍然会给你发送WM_PAINT,但是请注意,系统会自动帮你半透明。你不需要更改任何代码就能够实现半透明效果。

总的来说就是:除非程序中调用了InvalidateRect之类的函数,否则系统将接管窗口的绘制,用户收不到WM_PAINT消息

这个图片可以帮你理解:7197240_1309231955b128

For faster and more efficient animation or if per-pixel alpha is needed, call UpdateLayeredWindow. UpdateLayeredWindow should be used primarily when the application must directly supply the shape and content of a layered window, without using the redirection mechanism the system provides through SetLayeredWindowAttributes. In addition, using UpdateLayeredWindow directly uses memory more efficiently, because the system does not need the additional memory required for storing the image of the redirected window. For maximum efficiency in animating windows, call UpdateLayeredWindow to change the position and the size of a layered window. Please note that after SetLayeredWindowAttributes has been called, subsequent UpdateLayeredWindow calls will fail until the layering style bit is cleared and set again.

这段很重要!这俩函数是互相冲突的!想为窗口的每个像素设置Alpha通道你就直接调用UpdateLayeredWindow ,请不要调用SetLayeredWindowAttributes. 如果你想要重置,那就清除WS_EX_LAYERED然后重新设置!

Hit testing of a layered window is based on the shape and transparency of the window. This means that the areas of the window that are color-keyed or whose alpha value is zero will let the mouse messages through. However, if the layered window has the WS_EX_TRANSPARENT extended window style, the shape of the layered window will be ignored and the mouse events will be passed to other windows underneath the layered window.

碰撞测试(就是测试你的键鼠操作是不是落在这个窗体上)取决于你的透明度!也就是说:透明度为完全透明的窗体部分会被“穿透”。如果你想要整个窗体都被“穿透”,请设置WS_EX_TRANSPARENT属性!

——Come From MSDN

 

WM_NCHITTEST & return HTTRANSPARENT

了解这一部分的时候请注意了:这一部分不能实现穿透功能(准确的说是他的穿透功能很有限)。

首先我们先了解一下这个 WM_NCHITTEST 是什么玩意儿:

The WM_NCHITTEST message is sent to a window in order to determine what part of the window corresponds to a particular screen coordinate. This can happen, for example, when the cursor moves, when a mouse button is pressed or released, or in response to a call to a function such as WindowFromPoint. If the mouse is not captured, the message is sent to the window beneath the cursor. Otherwise, the message is sent to the window that has captured the mouse.

这个消息主要用于帮助系统判断窗体的哪一部分被点到了,哪一部分被操作了。

它的返回值有一条是这样的:

HTTRANSPARENT

In a window currently covered by another window in the same thread (the message will be sent to underlying windows in the same thread until one of them returns a code that is not HTTRANSPARENT).

这段我也不翻译了,相信你也注意到了“in the same thread ”已经连续出现了两次了!微软到底在强调什么呢…….呵呵

SetLayeredWindowAttributes

函数说明:
BOOL SetLayeredWindowAttributes(
  __in  HWND hwnd,
  __in  COLORREF crKey,
  __in  BYTE bAlpha,
  __in  DWORD dwFlags
);
\\这函数用于设置窗口的alpha混合(不透明度)
\\实现出来的效果是整个窗体都以某一透明度显示
函数参数:

hwnd [in]
HWND

Handle to the layered window. A layered window is created by specifying WS_EX_LAYERED when creating the window with the CreateWindowEx function or by setting WS_EX_LAYERED via SetWindowLong after the window has been created.

拥有WS_EX_LAYERED 属性的窗口句柄。

crKey [in]
COLORREF

COLORREF structure that specifies the transparency color key to be used when composing the layered window. All pixels painted by the window in this color will be transparent. To generate a COLORREF, use the RGB macro.

具体说起来又要长篇大论了,简单来说就是透明窗体的底色是什么??我们不需要底色,只需要能够看到该窗体下面的另一个窗体,我们就可以忽略这个参数

bAlpha [in]

BYTE

Alpha value used to describe the opacity of the layered window. Similar to the SourceConstantAlpha member of the BLENDFUNCTION structure. When bAlpha is 0, the window is completely transparent. When bAlpha is 255, the window is opaque.

该值为0全透明,该值为255不透明。为什么是255呢??

该参数是BYTE类型 只有8位可存储来着 你把 11111111 转换为十进制就是255。

dwFlags [in]
DWORD

Specifies an action to take. This parameter can be one or more of the following values.

指定哪个参数我们使用了,因为我们忽略了crKey参数,所以我们仅仅指定LWA_ALPHA就可以了。

LWA_COLORKEY

Use crKey as the transparency color.

LWA_ALPHA

Use bAlpha to determine the opacity of the layered window.

函数返回:

BOOL

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero. To get extended error information, call GetLastError.

不解释,你懂得。

需要注意:

Note that once SetLayeredWindowAttributes has been called for a layered window, subsequent UpdateLayeredWindow calls will fail until the layering style bit is cleared and set again.

什么?你问我这一段意思是什么????那就请你重头认真看我的教程,不要走马观花!

 

UpdateLayeredWindow

函数说明:
BOOL UpdateLayeredWindow(
  __in  HWND hwnd,
  __in  HDC hdcDst,
  __in  POINT *pptDst,
  __in  SIZE *psize,
  __in  HDC hdcSrc,
  __in  POINT *pptSrc,
  __in  COLORREF crKey,
  __in  BLENDFUNCTION *pblend,
  __in  DWORD dwFlags
)\\该函数更新分层窗口的位置、大小、形状、内容、透明度
函数参数:

hwnd [in]
HWND

Handle to a layered window. A layered window is created by specifying WS_EX_LAYERED when creating the window with the CreateWindowEx function.

hdcDst [in]
HDC

Handle to a DC for the screen. This handle is obtained by specifying NULL when calling the function. It is used for palette color matching when the window contents are updated. If hdcDst is NULL, the default palette will be used.

输出到的DC句柄(GetDC(NULL)),你也可以写NULL使用默认调色盘(不改变内容)。

If hdcSrc is NULL, hdcDst must be NULL.

pptDst [in]
POINT

Pointer to a POINT structure that specifies the new screen position of the layered window. If the current position is not changing, pptDst can be NULL.

更新位置信息(写NULL表示不移动)

psize [in]
SIZE

Pointer to a SIZE structure that specifies the new size of the layered window. If the size of the window is not changing, psize can be NULL. If hdcSrc is NULL, psize must be NULL.

更新大小信息(写NULL表示不缩放)

hdcSrc [in]
HDC

Handle to a DC for the surface that defines the layered window. This handle can be obtained by calling the CreateCompatibleDC function. If the shape and visual context of the window are not changing, hdcSrc can be NULL.

内容信息(用于更新窗口颜色)

pptSrc [in]
POINT

Pointer to a POINT structure that specifies the location of the layer in the device context. If hdcSrc is NULL, pptSrc should be NULL.

从hdcSrc的哪个位置开始更新?

crKey [in]
COLORREF

COLORREF structure that specifies the color key to be used when composing the layered window. To generate a COLORREF, use the RGB macro.

这个,同上个函数。

pblend [in]
BLENDFUNCTION

Pointer to a BLENDFUNCTION structure that specifies the transparency value to be used when composing the layered window.

用于更新alpha(我们将在下面给出详解

dwFlags [in]
DWORD

This parameter can be one of the following values.

同上个函数

ULW_ALPHA

Use pblend as the blend function. If the display mode is 256 colors or less, the effect of this value is the same as the effect of ULW_OPAQUE.

ULW_COLORKEY

Use crKey as the transparency color.

ULW_OPAQUE

Draw an opaque layered window.

If hdcSrc is NULL, dwFlags should be zero.

函数返回:

If the function succeeds, the return value is nonzero.

If the function fails, the return value is zero.

需要注意:
  • 更新一次就像永久更改了整个窗体的位图一样
  • 效率较低
  • 系统自动重新绘制
  • 没有WM_PAINT

最后是极其重要的内容:BLENDFUNCTION参数的讲解:

基本概念:

BLENDFUNCTION结构体

AlphaBlend是Window自带的GDI函数,在作GUI的时候为了达到更漂亮的效果我们常常用它。 

这种结构的混合控制通过指定源和目标位图的混合功能。 

typedef struct _BLENDFUNCTION {

BYTE BlendOp;

BYTE BlendFlags;

BYTE SourceConstantAlpha;

BYTE AlphaFormat;

} BLENDFUNCTION, *PBLENDFUNCTION, *LPBLENDFUNCTION;

BlendOp 指定源混合操作。目前,唯一的源和目标的混合方式已定义为AC_SRC_OVER; 

BlendFlags 必须是0; 

SourceConstantAlpha 指定一个alpha透明度值,这个值将用于整个源位图;该SourceConstantAlpha值与源位图的每个像素的alpha值组合;如果设置为0,就会假定你的图片是透明的;如果需要使用每像素本身的alpha值,设置SourceConstantAlpha值255(不透明); 

AlphaFormat 这个参数控制源和目标的解析方式,AlphaFormat参数有以下值:

AC_SRC_ALPHA: 这个值在源或者目标本身有Alpha通道时(也就是操作的图本身带有透明通道信息时),提醒系统API调用函数前必须预先乘以alpha值,也就是说位图上某个像素位置的red、green、blue通道值必须先与alpha相乘。例如,如果alpha透明值是x,那么red、green、blue三个通道的值必须乘以x并且再除以255(因为alpha的值的范围是0~255),之后才能被调用。

 

应用备注:

1、当AlphaFormat参数的值是AC_SRC_ALPHA,那么源位图必须是32位深,否则的话,AlphaBland函数将调用失败

2、当BlendOp参数是AC_SRC_OVER时,源位图根据alpha透明度值直接覆盖在目标位图之上

3、如果源位图不带有透明度信息(那样的话,AC_SRC_ALPHA不设置),将由SourceConstanAlpha的值来决定如何混合源位图与目标位图,如下表中所示。表中SCA代表SourceConstantAlpha的值,同样,SCA除以了255,因为它的范围是从0到255.

Dst.Red = Src.Red * (SCA/255.0) + Dst.Red * (1.0 - (SCA/255.0))

Dst.Green = Src.Green * (SCA/255.0) + Dst.Green * (1.0 - (SCA/255.0))

Dst.Blue = Src.Blue * (SCA/255.0) + Dst.Blue * (1.0 - (SCA/255.0))

在这种情况下,如果目标位图有透明度信息,那么混合方式将按照下面的公式来:

Dst.Alpha = Src.Alpha * (SCA/255.0) + Dst.Alpha * (1.0 - (SCA/255.0))

4、如果源位图没有用SourceConstantAlpha参数值(那表示该参数等于255),每一个像素的透明度将决定源位图和目标位图的混合结果,如下所示:

Dst.Red = Src.Red + (1 - Src.Alpha) * Dst.Red

Dst.Green = Src.Green + (1 - Src.Alpha) * Dst.Green

Dst.Blue = Src.Blue + (1 - Src.Alpha) * Dst.Blue

在这种情况下,如果如果目标位图有透明度信息,那么混合方式将按照下面的公式来:

Dest.alpha = Src.Alpha + (1 - SrcAlpha) * Dst.Alpha

5、如果源位图既有SourceConstantAlpha值(也就是它的值不是255),每个像素又有透明度值,那么源位图的每一个像素将首先乘以SourceConstantAlpha的值,然后根据每个像素的透明度值混合,如下表中所示。同样,SourceConstantAlpha除以了255,因为它的范围是从0到255.

Src.Red = Src.Red * SourceConstantAlpha / 255.0;

Src.Green = Src.Green * SourceConstantAlpha / 255.0;

Src.Blue = Src.Blue * SourceConstantAlpha / 255.0;

Src.Alpha = Src.Alpha * SourceConstantAlpha / 255.0;

Dst.Red = Src.Red + (1 - Src.Alpha) * Dst.Red

Dst.Green = Src.Green + (1 - Src.Alpha) * Dst.Green

Dst.Blue = Src.Blue + (1 - Src.Alpha) * Dst.Blue

Dst.Alpha = Src.Alpha + (1 - Src.Alpha) * Dst.Alpha

——该部分来自《百度百科》

现在重点来了!

=>!!!前方高能请注意!!!<=

透明渐变&鼠标穿透的实现方法,实现效果如图:

捕获捕获2

=》传送门《=

posted @ 2015-05-23 17:23  小鸟喳喳叫  阅读(6639)  评论(0编辑  收藏  举报