Layered windows and UpdateLayeredWindow 分层窗口

前段摘自MSDN的详尽介绍:

Recently I was playing with transparent (layered) windows in Windows XP.
The basic information about layered windows is available from MSDN,
however the lack of information and examples for UpdateLayeredWindow() inspired me
to write this article.

To create a layered window flag WS_EX_LAYERED must be used.

//here we create a layered window
HWND hWnd = CreateWindowEx(WS_EX_TOPMOST | WS_EX_LAYERED, WNDCLASSNAME,WNDCAPTION, WS_POPUP ,WINDOW_X, WINDOW_Y, WINDOW_WIDTH, WINDOW_HEIGHT,NULL, NULL, hInstance, NULL);

It is also possible to set layered flag after creation of a regular window.

//setting extended style flag WS_EX_LAYERED);
SetWindowLong(hWnd, GWL_EXSTYLE, GetWindowLong(hWnd, GWL_EXSTYLE) | WS_EX_LAYERED);

In short, there are two different methods to work with layered windows under Window 2000 and XP.


Using SetLayeredWindowAttributes()

The idea behind this is that you can easy create a semi transparent window and draw on this window
as you used to draw in a regular Win 32 API applications.

Application processes all regular window messages such as WM_PAINT and WM_ERASEBKGND .
SetLayeredWindowAttributes() can be used in order to change window transparency.
The function works in two alternative modes.
First mode allows to set transparency of pixels of a given color but all other pixels will be absolutely opaque.

The following code makes all white pixels on window 100% transparent

SetLayeredWindowAttributes(hWnd, RGB(0xff, 0xff, 0xff), 0, LWA_COLORKEY);
//                                                                  ^                      ^
//                                                                  |                      |
//                                                             color key          NOT USED with LWA_COLORKEY


Another mode allows to set whole window transparency for all differently colored pixels.
This example makes window 50% transparent.

SetLayeredWindowAttributes(hWnd, RGB(0xff, 0xff, 0xff), 0x7F, LWA_ALPHA);
//                                                            ^                               ^
//                                                            |                               |
//                                  not used with LWA_ALPHA     opacity level (0 to 255)


What is good and bad about this method?


+ It is easy and intuitive

- It does not allow to set per pixel transparency for complex windows.

- The performance of window drawing with SetLayeredWindowAttributes() is relatively low

Using UpdateLayeredWindow()

This is another method of drawing transparent windows which works relatively faster and it offers more power for programmer.
This function does anything you want for a layered window.
You can change position, size, look and transparency all in one function call.
Also this function can use per pixel Alpha information stored in a bitmap to maintain pixel level transparency.

Note a few moments before using UpdateLayeredWindow().

  1. If you first call SetLayeredWindowAttributes() before UpdateLayeredWindow() you will get errors (0×06).
  2. Since MSDN unclearly says that hdcDst parameter is a “Handle to a device context (DC) for the screen” this actually means a window DC. I got error (number 6) trying to use screen DC.

So the basic scheme is the following.
In a timer message handler I draw the window contents on a memory DC.
Then I update window surface using this function.

//
	// this function is called when hdcBackBuffer bitmap
	// contains a fresh look of the window
	//
	void __fastcall UpdateMemoWindow(HWND hWnd){
		HDC hRealDC = GetDC(hWnd);

			BLENDFUNCTION bfunc;
			bfunc.AlphaFormat = 0;
			bfunc.BlendFlags = 0;
			bfunc.BlendOp = AC_SRC_OVER;
			bfunc.SourceConstantAlpha = ActiveSettings.Opacity;

			if (ActiveSettings.TextOnly){

				//
				// BLENDFUNCTION &bfunc is not used in this case
				// because we need to maintain color level opacity
				//
				if (!UpdateLayeredWindow(hWnd, hRealDC, &gWindowPosition, &gWindowSize, \
					 hdcBackBuffer, &PointZero, RGB(255,255,255), &bfunc, ULW_COLORKEY \
					)){
					HandleError(L"Failed to update layered window");
				}
			}else{

				//
				// the color key RGB(255,255,255) is not used in this case
				// because transparency will be based on pixels of the bitmap
				// selected for hdcBackBuffer
				//
				if (!UpdateLayeredWindow(hWnd, hRealDC, &gWindowPosition, &gWindowSize, \
					 hdcBackBuffer, &PointZero, RGB(255,255,255), &bfunc, ULW_ALPHA \
					)){
					HandleError(L"Failed to update layered window");
				}
			}

		ReleaseDC(hWnd, hRealDC);
	}
网上寻找到的关于分层窗口的总结:
1、主要是为了实现透明效果而加的一个窗口样式(WS_EX_LAYERED)
使用 UpdateLayeredWindow 之后,窗口就忽略了 WM_PAINT消息,所有DC绘制都要自己操作。
2、win2000以后的版本在用户界面方面包括了几个重大的改进,譬如有阴影的鼠标,渐入的工具条快速提示,透明的窗口,平滑的窗口变化等等.这些变化都可以归结为用户对"渐变"的需求远远要比传统的"跃进式"要感兴趣的多,很显然,"渐变"比"跃进式"的用户界面要温和的多.
其实是因为WIN2000采用了一种GDI,以前叫GDI2K,现在叫GDI+,是一种新型的图形设备接口,它的主要特点在于它那个创建全新的用户桌面体系,能够轻易的完成二维或三维的图形处理,同同时也提供了增强的图形处理技术,如alpha blending,纹理,贴图,增强的文本以及图片显式技术.实际上GDI+主要的特色就就在于强调通过硬件加速来达到靓号的视觉感受!
透明的窗口,淡入淡出,这些在很大程度上引入了多分层窗口的应用!
分层窗口主要作用以及特点如下:
分层窗口采取"合成"(compose)的方式来绘制,【系统占用资源低】,【支持窗口平滑变化】
分层窗口可以是【半透明】或者【透明】的
分层窗口可以是【任意形状】,支持【变形操作】
我们知道,在传统的windows98或者NT下面,窗口外观发生变化(该窗口被其它窗口覆盖,窗口大小发生变化)时,应用程序会自动维护窗口的外观,而这种维护,是在编程中加入了对WM_PAINT之类的消息响应.如果桌面窗口的外观频繁的发生变化,那么,所有的窗口都回去响应"WM_PAINT"消息,以保持窗口自身的外观,这样就使得每个窗口都在进行重绘操作,这样就自然加重了操作系统的负担,当系统忙不过来的时候,你会发现,有些窗口忧郁在重绘过程中就产生了"抖动"~~~大大的影响了整个桌面的外观.
分层窗口的出现使得上面的问题得到了解决,它的特点就在于,它将窗口的绘制操作进行了重新定义::【由操作系统(而不是应用程序),完成重绘操作,完成的方式是"合成":将窗口看成一副位图,窗口外形的变化只是"位图"的变化!而不需要非得通过对WM_PAINT消息来进行.这样就能够保证分层窗口在概念上包括两层含义:与传统相比,这种窗口从外观上看起来恩奇怪(它可以是透明或者半透明的,或者是异性的);二是【重定向】:对窗口的重绘操作不需要你手工添加代码来维护,系统会自动将重绘操作在后台完成!
分层窗口实际上一种在WIN2000下能够自动地与非活动窗口进行合成的一种窗口.
经过自己的实验,发现和文章还是有一些出入,最后自己做一点总结 :

  
总的来说,调用这两个函数中的任意一个之后,除非程序中调用了InvalidateRect之类的函数,否则系统将接管窗口的绘制,用户收不到WM_PAINT消息,
仿佛就是UpdateLayeredWindow永久改变了HDC的位图一样。而且单独调用UpdateLayeredWindow而没有调用过SetLayeredWindowAttributes的话
每次刷新必须依靠UpdateLayeredWindow,普通地再WM_PAINT中处理是无效的。而调用SetLayeredWindowAttributes

之后就能阻断UpdateLayeredWindow的这种行为。还有一个重要区别是单独UpdateLayeredWindow之后窗口第一次显示

的时候不会受到WM_PAINT.LayeredWindow窗口效率比较低,不适合大而复杂的窗口。

楼上那个兄弟是在初始化函数中(OnInitDialog,OnCreate...)调用了修改了窗口的style为WS_EX_LAYERED,然后紧接着
DrawUI(这个函数是他用来绘制的)。程序中没有调用SetLayeredWindowAttributes所以从始至终都没有收到过
WM_PAINT(如果没有InvalidateRect的话)。但是这对于习惯于在WM_PAINT中处理绘制代码的我不太习惯,所以我把 DrawUI放在WM_PAINT中了,但是收不到这个消息怎么办?可以在初始化中SetLayeredWindowAttributes这样就
能收到一次WM_PAINT在第一次显示的时候,但是SetLayeredWindowAttributes的 说明中MSDN: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.
所以这样也是不行的,所以可以选择在OnPaint中修改WS_EX_LAYERED属性而不是在初始化函数中。 
需要特别注意的是UpdateLayeredWindow和SetLayeredWindowAttributes的互斥性。
 
 
 
 
另:研究一下Layered Window
 
 

    前几天吴同学问我怎么做这样的透明效果:         

        开始想得很简单, 异型窗口+贴PNG图就搞定了, 仔细一看没这么简单, 这个钟表窗口, 边缘部分是透明的, 中间部分是不透明的, 如果是全透明窗口, 创建LayeredWindow之后调用SetLayeredWindowAttributes即可, 但现在这个部分透明的窗口, 是要用到LayeredWindow针对每个像素的特性, 传说中的东西, 一直没实践过.

         看了一下,也很简单, LayeredWindow提供两种模式:

          1,使用SetLayeredWindowAttributes去设置透明度, 完成窗口的统一透明,此时窗口仍然收到PAINT消息, 其他应用跟普通窗口一样.

          2, 使用UpdateLayeredWindow方法, 向系统提交包含bitmap的DC, 交由系统统一管理,此时再也收不到paint消息, 任何对窗口的改变,只能通过UpdateLayeredWindow来修改.

         

          如果你不需要针对像素级别的不同透明,只需要使用SetLayeredWindowAttributes模式即可,用法与普通窗口用法一样,有一点不同,系统会缓存窗口的bitmap,所以当窗口上面的其他窗口被移开时,这是系统会去自己绘制,不会发送paint消息。使用这种模式的好处时,你基本不用改变你使用窗口的方法,你收到paint消息后,绘制的图像会被系统重定向到另一个函数里面,进行组合,从而得出透明效果。

         如果你需要达到针对像素级别的不同透明,也就是上图的想过,或者你想更加直接的去控制窗口的绘制,就必须使用UpdateLayeredWindow方法了,这个方法不重定向你的绘制结果,也不缓存窗口的bitmap,而是完全由你自己来绘制,这样在内存上来说,是更高效的。需要注意的是,一旦你调用了SetLayeredWindowAttributes,UpdateLayeredWindow的调用就会失败,除非重新设置WS_EX_LAYERED,所以他们是互斥的。

         对layeredwindow来说,在不完全透明的地方,是可以接收到鼠标消息的,在完全透明的地方,鼠标的点击将会被穿过,还有一种情况是对窗口设置了WS_EX_TRANSPARENT属性,鼠标消息也会穿过。

        特别注意的是,WS_EX_LAYERED属性是不可以设置给子窗口的。

        看了一下QQ的个人信息展示栏,底部带有这种透明效果,抓起窗口,发现就是使用layeredwindow的UpdateLayeredWindow模式:         

        而百度Hi的信息栏,就没有这种效果了:

                 虽然也是采用的layeredwindow,但是只是为了渐隐效果采用的。采用的,是SetLayeredWindowAttributes模式。

        对于这种透明的效果,另外又有几种旁门左道可以实现,但终归有缺陷,总结一下,有如下几种:

        1,利用异形窗口实现,由于子窗口不能被设置WS_EX_LAYERED,所以,可以给定A、B、C三个窗口,其中,A为全透明窗口,B为UpdateLayeredWindow型的layered窗口,C为正常子窗口,C的父亲为A,B为A的popup窗口。这样,在C上面有不透明的东西,B上有任意透明的东西,唯一要做的,就是当ABC三窗口有任意一个移动或者改变大小时,其他两个都要相应变化。

         这个方案的缺点,一是不适合大规模使用,第二是有同事试验过,移动时如果移动过快会留下残影,这种方案适用于不移动窗口的简单程序。

         2,利用截图背景实现,窗口显示时,对窗口区域的屏幕进行截图,截完之后,设置为窗口的背景。这种方案的缺点,也是窗口不能移动,并且背后不能有动画。只能是一种假透明。此种方案适用于复杂程序,在手机上变成使用的比较多,因为手机的窗口很多是全屏,不移动。

猜你喜欢

转载自blog.csdn.net/QQ384697384/article/details/18774243
今日推荐