-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
透明窗口:实现方式 #4
Comments
子窗口透明https://blog.csdn.net/xcl119xcl/article/details/5574188 https://www.cnblogs.com/xnzzj/articles/4524085.html 子窗口的透明和主窗口完全不同, 主窗口是通过layeredwindow实现的, 而子窗口则必须自己实现, 尤其是半透明, 必须自己对获取的透明背景图进行操作。 要实现子窗口的透明需要做到下面几步:
另外一点, 最好同时截获parent窗的WM_PAINT消息, 在parent窗重画前调用InvalidateRect让本子窗口显示实效, 这样子窗口才能同样也收到一个WM_PAINT消息( 这样做是为了保险, 因为我不是很确定主窗口重画系统是否会自动给具有WS_EX_TRANSPARENT属性的子窗口同样发WM_PAINT ). 窗口创建
以上两种方法亲测可用,使用了最通用的Create方式 第一种重新spy++抓取确认了下,是不成功的;此种方式修改为WS_CHILD失败了,使用的是弹出窗口的形式;所以只能使用第二种方式来实现了 附manifest文件:
|
VC++实现窗口异形https://www.cnblogs.com/lmqweeds/archive/2012/07/23/2605319.html 由于工作的需要,最近一直在研究异形窗口的实现。网上也有一些相关的文章,能够满足各式各样的异形窗口要求。既然花了时间去研究,就想好好的将其总结记录下来,以免今后遇到类似问题,还要从新花时间去研究。
一、SetLayeredWindowAttributes函数 BOOL WINAPI SetLayeredWindowAttributes( __in HWND hwnd, __in COLORREF crKey, __in BYTE bAlpha, __in DWORD dwFlags ); hwnd 为需要变形的窗口句柄 代码实现如下:
在OnPaint()函数中添加如下代码:
二、UpdateLayeredWindow函数 该函数是根据PNG图像的透明值,自动生成相应的不规则窗口。但该函数生成的异形窗口不能为子窗口,同时,由于设置窗口为WS_EX_LAYERED风格,因此窗口类只有在第一次启动时才会调用OnPaint()函数,除非通过主动调用InvalidateRect(NULL,TRUE)函数。代码实现如下: 1.定义透明结构体
3.在OnPaint()函数中添加绘制代码:
三、使用HRGN区域组合创建 效率不高,比较慢!!
参考网址: 1.UpdateLayeredWindow函数: http://www.cnblogs.com/buffer/archive/2009/03/13/1410326.html 注意:
|
关于如何实现鼠标穿透窗口和窗口半透明https://www.cnblogs.com/xnzzj/articles/4524085.html 资料准备
窗口类型
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消息! 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然后重新设置! 碰撞测试(就是测试你的键鼠操作是不是落在这个窗体上)取决于你的透明度!也就是说:透明度为完全透明的窗体部分会被“穿透”。如果你想要整个窗体都被“穿透”,请设置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函数说明: 拥有WS_EX_LAYERED 属性的窗口句柄。 crKey [in] 具体说起来又要长篇大论了,简单来说就是透明窗体的底色是什么??我们不需要底色,只需要能够看到该窗体下面的另一个窗体,我们就可以忽略这个参数。 bAlpha [in] BYTE 该值为0全透明,该值为255不透明。为什么是255呢?? 该参数是BYTE类型 只有8位可存储来着 你把 11111111 转换为十进制就是255。 dwFlags [in] 指定哪个参数我们使用了,因为我们忽略了crKey参数,所以我们仅仅指定LWA_ALPHA就可以了。 LWA_COLORKEY Use crKey as the transparency color. LWA_ALPHA Use bAlpha to determine the opacity of the layered window. 函数返回: 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. 不解释,你懂得。 需要注意: 什么?你问我这一段意思是什么????那就请你重头认真看我的教程,不要走马观花! UpdateLayeredWindow函数说明: hdcDst [in] 输出到的DC句柄(GetDC(NULL)),你也可以写NULL使用默认调色盘(不改变内容)。 If hdcSrc is NULL, hdcDst must be NULL. pptDst [in] 更新位置信息(写NULL表示不移动) psize [in] 更新大小信息(写NULL表示不缩放) hdcSrc [in] 内容信息(用于更新窗口颜色) pptSrc [in] 从hdcSrc的哪个位置开始更新? crKey [in] 这个,同上个函数。 pblend [in] 用于更新alpha(我们将在下面给出详解) dwFlags [in] 同上个函数 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 fails, the return value is zero. 需要注意:
最后是极其重要的内容: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 |
鼠标穿透窗口 & 窗口渐变透明https://www.cnblogs.com/xnzzj/p/4524484.html 第一步:创建一个窗口(实现鼠标穿透)
第二步:创建包含Alpha信息的图片(实现渐变透明)
提醒一下:只有Alpha通道没有图像渲染出来还是什么都不是 这样编辑图像内容:第三步:使用图片实现渐变透明(实现渐变透明)
但是….请注意,第三步没有结束,这段代码是有问题的!!! 我们会发现输出的结果不对,为什么呢??让我们来看一个印度小伙的解释:
重要概念——预乘
预乘就是预先将图片的RGB通道乘以它的Alpha通道数值
公式:(单个颜色值*Alpha/255)
答案是为了节约系统在混合时需要进行的运算: 我们知道半透明混合的公式大概是这样的: 结果值 =( 数值1 * ( 1 - Alpha/255) + 数值2 * Alpha /255 ) (MSDN:Dst.Red= Src.Red+ (1 - Src.Alpha) * Dst.Red)(这里的Alpha是1以内的小数)(这里的SRC已经经过预乘了) 如果进行了预乘,显然 数值2 * Alpha /255 这一步就可以快一些了。 PS:其实还是因为微软要求这样搞,要不我才不愿意- - 哼…..
每次程序启动都要处理好麻烦,我就写了个预处理器,代码如下:
|
Controls and the Desktop Window Managerhttps://weblogs.asp.net/kennykerr/controls-and-the-desktop-window-manager Of all the Windows Vista for Developers series articles that I have written, the one about the Desktop Window Manager (DWM) is by far the most popular considering the blog traffic statistics and the amount of email I receive with questions about glass. By far the most common question I hear is about how to get controls to render correctly on glass. If you recall, I wrote the DWM article before the release of Windows Vista to manufacturing. Those early builds included a hack that allowed you to easily draw controls on glass by tweaking the transparency key. I demonstrated this technique in the article. Unfortunately, when Microsoft finally released Windows Vista this “hack” no longer worked leaving developers to wonder how it could be done. Having said my piece about DWM and what with being extremely busy with my day job and various other commitments I never quite found the time to present alternative solutions. For the sake of my email inbox, I am going to present a simple solution to address the most common request: How can I display an edit control on glass?! There are a number of ways of solving this sort of problem. Specifically, there are a number of ways to override the default drawing behavior of the standard and common controls. You can handle WM_PAINT and draw it yourself. This tends to be more work than it’s worth so most developers avoid it, however it does allow you all the freedom necessary to paint with the necessary alpha channel information to render correctly on glass. My DWM sample demonstrates this technique, albeit not with a control. Another solution is to owner draw controls. This is a lot of work but a lot less than the WM_PAINT approach as the system does a lot of the work for you. Owner draw is nice and works for most but not all controls. Notably it does not work with edit controls. An even simpler approach is custom draw but this works with an even smaller set of controls. Finally, for a small number of controls you can handle WM_CTLCOLORxxx to set the text and background color of the control. Considering the options listed thus far, only the last one actually supports edit controls and does not involve a gargantuan amount of work. Unfortunately, it does not play nicely with glass since the technique involves GDI primitives that do not support alpha blending. I repeat: How can I display an edit control on glass?! It is at times like these that I wonder why I don’t work for Microsoft and am not compensated for the articles I post on my blog… :) Having received yet another email yesterday asking about edit controls on glass I decided to poke around the Windows SDK to see if it has anything new to offer. By the way, if you do not regularly read the Windows SDK I highly encourage that you do so. On a hunch, I started my search in the Themes and Visual Styles section of the SDK. After all, this is the subsystem responsible for providing the look and feel for controls. The first thing I noticed was a set of functions added to UxTheme.dll in Windows Vista to support buffered painting. At first this might not seem all that interesting since the DWM already provides a degree of double buffering, but having the ability to buffer painting operations means that you can capture, alter, and then paint the updated bitmap. Of course, this is nothing new and could be achieved manually but the new functions provided by Visual Styles just simplify the task and solves our immediate problem quite nicely. The Buffered Paint API The buffered paint API provides a set of functions to provide efficient off-screen painting to a device context (DC). Since painting is done to a DC, it means that your investment in GDI is not lost. That’s right folks, you don’t have to port your entire application to Windows Presentation Foundation just yet. To start, you should call the BufferedPaintInit function at least once per thread to initialize the API for use. Each call to BufferedPaintInit must eventually be matched with a call to BufferedPaintUnInit on the same thread. To begin a buffered paint operation, simply call the BeginBufferedPaint function. It accepts a target DC and target rectangle identifying the eventual location that the buffer will be copied to. A few additional paramteters allow you to control the buffer characteristics. One of these is the format of the buffer and thankfully, it supports device-independent bitmaps (DIBs) necessary for most alpha blending operations. BeginBufferedPaint then returns a handle that can be passed to various other buffered paint API functions as well as a DC that you can use to paint to the buffer. One such function that you can pass the buffered paint handle to is BufferedPaintSetAlpha. It allows you to easily update the alpha channel for the entire buffer and set it to a single value to control the level of transparency/opacity. Note that it updates the alpha value for each pixel in the buffer to a single value. Finally you can copy the buffer to the target DC and free the associated resources allocated by BeginBufferedPaint with a single call to the EndBufferedPaint function. By now, you can probably imagine where I am going with this. You simply need to use the buffered paint API to create a buffer to paint the edit control into and then set the alpha channel on the buffer before updating the window DC. Let us bring it home with a sample. The BufferedPaint class The following class wraps the buffered paint API to simplify its use from C++. class BufferedPaint
private:
}; The OpaqueEdit class The following C++ class is used to subclass the system’s edit control so that we can more easily take over its painting duties. class OpaqueEdit :
private:
}; As you can see in my WM_PAINT handler, I send the edit control the WM_PRINTCLIENT message, instructing it to paint into the buffered DC. I then set the buffer’s alpha channel to 255 (completely opaque) and update the target DC. What might come as a surprise is the need for the EN_CHANGE handler. Since the edit control paints outside of the WM_PAINT message, I need to paint “over” the extra painting that occurs when the control’s text changes. In this example, I simply invalidate the control, which forces it to receive a new WM_PAINT message. I could probably optimize this further but it is sufficient for this example. Keep in mind that since the DWM provides double buffering automatically you should not notice any flicker due to the repeated painting. The SampleDialog class The following class simply ensures that the window is represented as a “sheet” of glass and uses the OpaqueEdit class to subclass the edit control. class SampleDialog :
private:
}; That is all there is to it. I hope the techniques described in this article will help in your path to adopting Windows Vista as a developer. |
使用 Windows 组合引擎实现高性能窗口分层https://msdn.microsoft.com/zh-cn/magazine/dn745861.aspx?f=255&MSPPError=-2147217396 自从我第一次看到 Windows XP 中的分层窗口,我就对它情有独钟。我好像一直对消除传统桌面窗口的矩形或近矩形边框非常感兴趣。后来出现了 Windows Vista。这个备受非议的 Windows 版本从一开始就预示着更具吸引力和灵活性的功能的降临。我们才开始认同 Windows Vista 带来的概念启发。虽然现在 Windows 8 出现了,但它也标志着分层窗口在慢慢地走下坡路。 Windows Vista 引入了一项名为“桌面窗口管理器”的服务。这个名字一直以来都具有误导性。请把它看作是 Windows 组合引擎或合成程序。此组合引擎完全改变了应用程序窗口在桌面上的呈现方式。每个窗口不是直接呈现给显示器或显示适配卡,而是呈现给屏外表面或缓冲区。系统为每个顶层窗口都分配了一个这样的表面,所有 GDI、Direct3D(当然还有 Direct2D)图形都呈现给此类表面。此类屏外表面称为重定向表面,因为 GDI 绘图命令以及 Direct3D 交换链呈现请求都会重定向或(在 GPU 中)复制到重定向表面。 在某些时候,不受任何指定窗口的制约,组合引擎会鉴于最新一批更改决定是否该组合桌面。这就涉及将所有重定向表面组合在一起、添加非工作区(通常称为窗口镶边)、添加一些阴影和其他效果以及将最终结果呈现给显示适配卡。 此组合过程具有许多显著优势,我将在接下来的几个月中一边深入探索 Windows 组合,一边详细说明这些优势;不过它也存在一个潜在的严重限制,即这些重定向表面不透明。大多数应用程序都可以接受此限制,而且从性能角度来看此限制也是非常有意义的,因为 alpha 值混合处理的费用高昂。但这就导致分层窗口遭到排斥。 如果我要开发分层窗口,就需要掀起一场性能冲击。我在专栏“使用 Direct2D 绘制分层窗口”(msdn.microsoft.com/magazine/ee819134) 中介绍了具体的体系结构限制。总而言之,分层窗口由 CPU 处理,主要用于支持 alpha 值混合处理像素的命中测试。也就是说,CPU 需要复制分层窗口的表面区域的组成像素。不管我是在 CPU 上呈现(往往比 GPU 呈现慢很多)还是在 GPU 上呈现,我都必须支付总线带宽税,因为我呈现的所有内容都必须从视频存储器复制到系统内存。在之前提到的专栏中,我还介绍了如何充分利用 Direct2D 尽可能发挥系统性能,因为我只有通过 Direct2D 才能在 CPU 和 GUP 呈现之间做出选择。存在的隐患就是,即使分层窗口一定要位于系统内存中,组合引擎也可以立即将其复制到视频存储器中,这样分层窗口的实际组合就仍为硬件加速。 虽然我无法带来传统分层窗口即将重返主导地位的希望,但我确实带来了一些好消息。传统的分层窗口具有两项特定的相关功能。第一项功能是每像素 alpha 值混合处理。我呈现给分层窗口的任何内容都会与桌面以及任意给定时刻窗口后面的所有内容进行 alpha 值混合处理。第二项功能是 User32 能够根据像素 alpha 值对分层窗口执行命中测试,允许鼠标消息在特定点的像素为透明时贯透过去。从 Windows 8 和 8.1 开始,虽然 User32 一直没有发生显著变化,但细微变化也有,即完全支持 GPU 上的每像素 alpha 值混合处理,且将窗口表面传输到系统内存的费用也取消了。也就是说,如果我不需要执行每像素命中测试,现在就能够生成分层窗口效果,同时不会对性能造成影响。整个窗口将统一执行命中测试。撇开命中测试不谈,只是这样就已经令我兴奋不已,因为这是系统显然可以执行的任务,但应用程序就从来没能利用这种功能。如果您对此感兴趣,请继续阅读本文,我将向您介绍这是如何实现的。 实现此功能的关键点在于使用 Windows 组合引擎。组合引擎一开始作为桌面窗口管理器出现在 Windows Vista 中,它包含受限制的 API 并具有流行的半透明 Aero 毛玻璃效果。接下来 Windows 8 出现了,它引入了 DirectComposition API。此 API 适用于相同的组合引擎,只是受限制程度更低。随着 Windows 8 版本的发布,Microsoft 最终允许第三方开发人员利用这一面世已久的组合引擎。当然,您还需要使用由 Direct3D 强力驱动的图形 API(如 Direct2D)。但您首先需要处理不透明的重定向表面。 正如我之前提到的,系统为每个顶层窗口都分配了一个重定向表面。从 Windows 8 开始,您现在可以创建顶层窗口,并请求创建没有重定向表面的顶层窗口。严格来说,这与分层窗口并不相关。因此,请勿使用 WS_EX_LAYERED 扩展的窗口样式(实际上,分层窗口的相关支持在 Windows 8 中有了细微改进,不过我将在即将发表的专栏文章中深入介绍这些改进)。您需要改用 WS_EX_NOREDIRECTIONBITMAP 扩展的窗口样式,此样式可以指示组合引擎不为窗口分配重定向表面。我将从简单的传统桌面窗口入手。图 1 中的示例介绍了如何填充 WNDCLASS 结构、注册窗口类、创建窗口以及抽取窗口消息。其中并没有什么新内容,但这些基本原理仍然至关重要。窗口变量处于未使用状态,但您马上就需要使用这个变量。您可以将此变量复制到 Visual Studio 内的 Visual C++ 项目中,也可以只是从以下命令提示符编译此变量:
图 1:创建传统窗口
图 2 展示了窗口在我的桌面上的显示效果。请注意,此示例没有什么特别之处。虽然该示例未提供任何绘制和呈现命令,但窗口的工作区不透明,并且组合引擎添加了非工作区、边框和标题栏。要应用 WS_EX_NOREDIRECTIONBITMAP 扩展的窗口样式删除不透明的重定向表面(表示此工作区),只需使用接受扩展的窗口样式的主要参数将 CreateWindow 函数切换为 CreateWindowEx 函数即可:
图 2:桌面上的传统窗口 变化只包括添加了主要参数、WS_EX_NOREDIRECTIONBITMAP 扩展的窗口样式,以及使用了 CreateWindowEx 函数(而不是更简单的 CreateWindow 函数)。不过,桌面上呈现的结果变化更为彻底。图 3 展示了窗口在我的桌面上的显示效果。请注意,窗口的工作区现在是完全透明。移动窗口可以说明这一点。我甚至可以在背景中播放视频,任何部分都不会被遮挡。另一方面,整个窗口统一执行命中测试,当您在工作区内单击时,不会失去窗口焦点。这是因为负责命中测试和鼠标输入的子系统没有意识到工作区是透明的。 当然,接下来您会问,如果没有可向组合引擎提供的重定向表面,如何能够将任意内容呈现给窗口?答案就是利用 DirectComposition API 及其与 DirectX 图形基础结构 (DXGI) 的深度集成。此技术同样可强力驱动 Windows 8.1 XAML 实施,从而在 XAML 应用程序内提供性能极高的内容组合。Internet Explorer Trident 呈现引擎也将 DirectComposition 广泛用于触控平移、缩放、CSS3 动画、切换和转换。 我就是要用它来组合通过每像素预乘 alpha 值支持透明度的交换链,并将其与桌面的其余部分混合。传统的 DirectX 应用程序通常使用 DXGI 工厂的 CreateSwapChainForHwnd 方法创建交换链。此交换链受到在呈现过程中有效交换的一对或一组缓冲区的支持,允许应用程序在前一帧得到复制的同时呈现下一帧。应用程序呈现到的交换链表面是不透明的屏外缓冲区。当应用程序呈现交换链时,DirectX 会将内容从交换链的后台缓冲区复制到窗口的重定向表面。正如我之前提到的,组合引擎最终会将所有的重定向表面都组合到一起,从而形成整个桌面。 在这种情况下,由于窗口不包含任何重定向表面,因此不能使用 DXGI 工厂的 CreateSwapChainForHwnd 方法。不过,我仍然需要使用交换链来支持 Direct3D 和 Direct2D 呈现。这正是 DXGI 工厂的 CreateSwapChainForComposition 方法的用途所在。我可以使用此方法创建一个无窗口的交换链及其缓冲区,但呈现此交换链不会将位数复制到不存在的重定向表面,而是直接向组合引擎提供。然后,组合引擎可以获取此表面,直接用它来取代窗口的重定向表面。由于此表面不是不透明的,而是像素格式完全支持每像素预乘 alpha 值,因此结果就是在桌面上进行完全适合像素的 alpha 值混合处理。速度也极快,这是因为不必在 GPU 内进行复制,进而也就不必通过总线复制到系统内存。 这些都是理论。现在是进行实践的时候了。由于 DirectX 是一种 COM 对象,因此我将使用 Windows 运行时 C++ 模板库中的 ComPtr 类模板来管理接口指针。我还需要添加并关联至 DXGI、Direct3D、Direct2D 和 DirectComposition API。以下代码展示了这是如何实现的:
我通常会在预编译头中添加这些代码。在这种情况下,我会省略 using 指令,并且只在我的应用程序的源文件中添加此指令。 我不喜欢错误处理通篇存在并且偏离主题本身细节的示例代码,因此我将使用一个异常类和 HR 函数妥善处理错误检查。图 4 中展示了一个简单的实施示例,当然您也可以自行决定错误处理策略。 图 4:将 HRESULT 错误转变为异常 struct ComException { HRESULT result; ComException(HRESULT const value) :result(value) {} }; void HR(HRESULT const result) { if (S_OK != result) { throw ComException(result); } } 我现在可以开始组装呈现堆栈,自然就从 Direct3D 设备入手了。我将会快速介绍此主题,因为我已经在 2013 年 3 月的专栏“Direct2D 1.1 简介”(msdn.microsoft.com/magazine/dn198239) 中详细介绍了 DirectX 基础结构。以下是 Direct3D 11 接口指针: ComPtr direct3dDevice; HR(D3D11CreateDevice(nullptr, // 适配卡 D3D_DRIVER_TYPE_HARDWARE, nullptr, // 模块 D3D11_CREATE_DEVICE_BGRA_SUPPORT, nullptr, 0, // 最高可用功能级别 D3D11_SDK_VERSION, &direct3dDevice, nullptr, // 实际功能级别 nullptr)); // 设备上下文 ComPtr dxgiDevice; HR(direct3dDevice.As(&dxgiDevice)); ComPtr dxFactory; HR(CreateDXGIFactory2( DXGI_CREATE_FACTORY_DEBUG, __uuidof(dxFactory), reinterpret_cast<void **>(dxFactory.GetAddressOf()))); DXGI_SWAP_CHAIN_DESC1 description = {}; description.Format = DXGI_FORMAT_B8G8R8A8_UNORM; description.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; description.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; description.BufferCount = 2; description.SampleDesc.Count = 1; description.AlphaMode = DXGI_ALPHA_MODE_PREMULTIPLIED; 必须将交换链的缓冲区使用设置为允许将呈现器目标输出定向到它。您必须进行此设置,这样 Direct2D 设备上下文才能创建位图来使用绘图命令定位 DXGI 表面。Direct2D 位图本身只是受到交换链支持的抽象概念。 组合交换链仅支持依序翻转的交换效果。这就是交换链取代重定向表面与组合引擎相关联的方式。在翻转模式下,所有缓冲区都直接与组合引擎共享。然后,组合引擎可以直接从交换链后台缓冲区组合桌面,无需进行其他任何复制操作。通常情况下,这是最有效的模式。组合也需要使用此模式,因此这就是我使用的模式。翻转模式也需要至少两个缓冲区,但不支持多重采样,因此将 BufferCount 设置为 2,并将 SampleDesc.Count 设置为 1。此计数是每像素的多重采样数量。将它设置为 1 可以有效禁用多重采样。 最后需要说明的是,alpha 模式至关重要。不透明的交换链通常会忽略 alpha 模式,但在本示例中,我确实想将透明行为包括在内。预乘的 alpha 值通常会带来最佳性能,而且它也是翻转模式支持的唯一选项。 在我可以创建交换链之前必须完成的最后一项操作是确定缓冲区的预期大小。调用 CreateSwapChainForHwnd 方法时,我通常会忽略大小,但 DXGI 工厂会向窗口查询工作区的大小。在这种情况下,DXGI 不知道我打算对交换链做什么,因此我需要告诉它所需的具体大小。在窗口创建后,您可以轻松查询窗口的工作区并相应地更新交换链说明: RECT rect = {}; GetClientRect(window, &rect); description.Width = rect.right - rect.left; description.Height = rect.bottom - rect.top; ComPtr swapChain; HR(dxFactory->CreateSwapChainForComposition(dxgiDevice.Get(), &description, nullptr, // 不限制 swapChain.GetAddressOf())); 图 5:使用 Direct2D 绘制到交换链 // 使用调试信息创建单线程 Direct2D 工厂 ComPtr d2Factory; D2D1_FACTORY_OPTIONS const options = { D2D1_DEBUG_LEVEL_INFORMATION }; HR(D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, options, d2Factory.GetAddressOf())); // 创建与 Direct3D 设备恢复关联的 Direct2D 设备 ComPtr d2Device; HR(d2Factory->CreateDevice(dxgiDevice.Get(), d2Device.GetAddressOf())); // 创建作为实际呈现器目标 // 并揭示绘图命令的 Direct2D 设备上下文 ComPtr dc; HR(d2Device->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_NONE, dc.GetAddressOf())); // 检索交换链的后台缓冲区 ComPtr surface; HR(swapChain->GetBuffer( 0, // 索引 __uuidof(surface), reinterpret_cast<void **>(surface.GetAddressOf()))); // 创建指向交换链表面的 Direct2D 位图 D2D1_BITMAP_PROPERTIES1 properties = {}; properties.pixelFormat.alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; properties.pixelFormat.format = DXGI_FORMAT_B8G8R8A8_UNORM; properties.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET | D2D1_BITMAP_OPTIONS_CANNOT_DRAW; ComPtr bitmap; HR(dc->CreateBitmapFromDxgiSurface(surface.Get(), properties, bitmap.GetAddressOf())); // 将设备上下文指向位图以进行呈现 dc->SetTarget(bitmap.Get()); // 绘制内容 dc->BeginDraw(); dc->Clear(); ComPtr brush; D2D1_COLOR_F const brushColor = D2D1::ColorF(0.18f, // 红色 0.55f, // 绿色 0.34f, // 蓝色 0.75f); // alpha HR(dc->CreateSolidColorBrush(brushColor, brush.GetAddressOf())); D2D1_POINT_2F const ellipseCenter = D2D1::Point2F(150.0f, // x 150.0f); // y D2D1_ELLIPSE const ellipse = D2D1::Ellipse(ellipseCenter, 100.0f, // x 半径 100.0f); // y 半径 dc->FillEllipse(ellipse, brush.Get()); HR(dc->EndDraw()); // 为组合引擎提供交换链 HR(swapChain->Present(1, // 同步 0)); // 标志 DirectComposition 主要用于将不同的位图组合在一起。与 Direct2D 一样,这里的位图概念在更大程度上是一个抽象概念,可允许不同的呈现堆栈互相合作,共同带来顺畅且具有吸引力的应用程序用户体验。 类似于 Direct3D 和 Direct2D,DirectComposition 是由 GPU 支持且强力驱动的 DirectX API。DirectComposition 设备是通过重新指向 Direct3D 设备而创建,与 Direct2D 设备重新指向基础 Direct3D 设备的方式基本相同。我使用之前用来创建交换链的同一 Direct3D 设备和 Direct2D 呈现器目标来创建 DirectComposition 设备: ComPtr dcompDevice; HR(DCompositionCreateDevice( dxgiDevice.Get(), __uuidof(dcompDevice), reinterpret_cast<void **>(dcompDevice.GetAddressOf()))); 接下来,我需要创建与视觉对象相关联的组合目标,这些视觉对象将与目标(即应用程序窗口)一起组合: ComPtr target; HR(dcompDevice->CreateTargetForHwnd(window, true, // 顶级 target.GetAddressOf())); ComPtr visual; HR(dcompDevice->CreateVisual(visual.GetAddressOf())); HR(visual->SetContent(swapChain.Get())); HR(target->SetRoot(visual.Get())); HR(dcompDevice->Commit()); 图 6 展示了在 Direct2D 已呈现给交换链且 DirectComposition 已将部分透明的交换链提供给组合引擎之后的应用程序窗口外观。 图 6:DirectComposition 表面上的 Direct2D 绘制 我终于找到了老问题的解决方案,我感到非常兴奋:可以生成与桌面的其余部分进行 alpha 值混合处理的高性能窗口。让我激动不已的是 DirectComposition API 的功能及其如何影响在本机代码中设计和开发应用程序用户体验的未来。 想要绘制您自己的窗口镶边吗?没问题,只需在创建窗口时将 WS_OVERLAPPEDWINDOW 窗口样式替换为 WS_POPUP 窗口样式即可。祝您工作愉快! Kenny Kerr 是加拿大的一名计算机程序员,也是 Pluralsight 的作者以及 Microsoft MVP。他的博客网址是 kennykerr.ca,您可以通过 Twitter twitter.com/kennykerr 关注他。 衷心感谢以下技术专家对本文的审阅:Leonardo Blanco (Microsoft) |
使用 Direct2D 绘制分层窗口https://msdn.microsoft.com/magazine/ee819134?f=255&MSPPError=-2147217396 在 Direct2D 上我第三个分期付款,我将展示其不匹配的电源的一些谈到的互操作性。 而不是彻底详细描述各种互操作性的所有选项 Direct2D 提供,我将引导您完成实用的应用程序:分层的窗口。 分层的窗口是那些已周围很长时间但 haven’t 发展大部分并因此需要特别小心,要有效地使用与现代图形的技术的 Windows 功能之一。 本文中我将假定您有基本的熟悉 Direct2D 编程。 如果不是,从六月 ( msdn.microsoft.com/magazine/dd861344 ) 读取我先前的文章和九月 ( msdn.microsoft.com/magazine/ee413543 ) 发出引入的编程和绘图 Direct2D 与基础知识的建议。 最初,分层的窗口提供几个不同的用途。 在具体的方式而言它们可用于轻松、 高效地生成视觉效果和无闪烁的呈现。 在 GDI 时已产生图形的主要方法将内容天什么内,这是真实的奖金。 在今天已经硬件加速世界上,但是,它不再引人注目因为分层的窗口仍属于 User32/GDI 的世界,并且没有更新任何重大的方式支持 DirectX,Microsoft 平台的高性能和高质量的图形。 分层的窗口提供唯一的撰写一个窗口在桌面上使用每像素 alpha 混合,它不能以其他方式与 Windows SDK 才能实现的能力。 我应该更不用说有真正两种类型的分层窗口。 区别涉及到您是否需要每像素不透明度控件向下或只是需要控制整个窗口的不透明度。 本文是前者但如果实际上只是需要控制一个窗口的不透明度有关可以简单地创建设置 alpha 值窗口后调用 SetLayeredWindowAttributes 函数来执行此操作。 Verify(SetLayeredWindowAttributes( 图 1 窗口具有 Alpha 值 因此涉及什么?嗯,在基本级别它十分简单。首先,需要填充 UPDATELAYEREDWINDOWINFO 结构中。此结构提供位置和大小的分层的窗口,以及定义窗口图面一个 GDI 设备上下文 (DC) — 和其中位于该问题。dc 属于旧的世界的 GDI 和是远从世界的 DirectX 和硬件加速。在稍后的上的更多。 除了需要分配您自己的结构的指针的完整,UPDATELAYEREDWINDOWINFO 结构 isn’t 完全记录在 Windows SDK,使其小于明显使用中。在所有,需要分配五种结构。有 ’s 源位置标识将从 DC 复制位图的位置。有 ’s 窗口位置标识窗口中一次更新在桌面上的放置位置。有 ’s 它还定义窗口的大小将复制,位图的大小: POINT sourcePosition = {}; BLENDFUNCTION blend = {}; 在 SourceConstantAlpha 但是,是有趣的您可以使用它在很大程度相同的方式,您可能使用 SetLayeredWindowAttributes 函数来控制整个窗口的不透明度。 当设置为 255 分层的窗口将仅使用每个像素的 alpha 值,但您可以调整它向零,或完全透明生成如淡入淡出的重绘成本的情况下窗口中或签出的效果。 它现在应该很明显 BLENDFUNCTION 结构将被命名为它的原因:得到的 alpha 混合窗口是此结构已经值的函数。 上次,’s 一起将 UPDATELAYEREDWINDOWINFO 结构: UPDATELAYEREDWINDOWINFO info = {}; 最后,您需要提供到源 DC 句柄和调用 UpdateLayeredWindowIndirect 函数以更新窗口: info.hdcSrc = sourceDC; Verify(UpdateLayeredWindowIndirect( 图 2 的 LayeredWindowInfo 包装类 class LayeredWindowInfo { public: LayeredWindowInfo(
void Update(
} UINT GetWidth() const { return m_size.cx; } UINT GetHeight() const { return m_size.cy; } 图 3 分层窗口框架 class LayeredWindow : LayeredWindowInfo m_info; public: BEGIN_MSG_MAP(LayeredWindow) LayeredWindow() :
} void Render() {
} void OnDestroy() { 图 4 创建 DIB BITMAPINFO bitmapInfo = {}; void* bits = 0; CBitmap bitmap(CreateDIBSection( 尽管不是必需的但在本例中我倾向于使用自上而下的位图,则由大多数的现代的图像处理组件在 Windows 中使用该格式,从而提高了互操作性。 这还产生一个正的步幅,计算如下: UINT stride = (width * 32 + 31) / 32 * 4; 尽管您可能位图数据直接传递到 GDI +,let’s 相反 DC 为其创建由于需要它仍要传递给 UpdateLayeredWindowIndirect 函数。 若要创建该 DC,调用创建内存与桌面兼容的 DC 在恰当命名的 CreateCompatibleDC 函数。 然后可以调用,以选择到 DC 的位图 SelectObject 函数。 GdiBitmap 的包装类,在 的 图 5 中环绕所有这样的并提供了一些额外的维护管理。 图 5 的 DIB 包装类 class GdiBitmap { CDC m_dc; public: GdiBitmap(__in UINT width,
} ~GdiBitmap() { UINT GetWidth() const { UINT GetHeight() const { UINT GetStride() const { void* GetBits() const { HDC GetDC() const { 图 6 GDI 分层窗口 class LayeredWindow : LayeredWindowInfo m_info; public: void Render() {
} class LayeredWindow : Window {
} 图形处理单元 (GPU) 首选以获得最佳性能的专用的内存。 到往往比系统内存中的两个位置之间复制要慢得多的 GPU 内存这意味着如果希望处理现有的位图它需要复制从系统内存 (RAM)。 在相反,也是这样:如果创建呈现位图使用该 GPU 然后决定将其复制到 ’s 昂贵的复制操作的系统内存。 通常这应该不会由该 GPU 呈现位图通常发送到显示设备的直接。 分层窗口的情况下该位图必须旅行返回到系统内存由于 User32/GDI 资源涉及到内核模式和用户模式需要访问该位图的资源。 渚嬪考虑 User32 需要命中测试事实分层窗口。 命中测试的分层窗口基于 alpha 的值,该位图允许通过鼠标消息,如果在某个特定时刻像素是透明。 作为结果的位图副本系统内存中需要允许发生这种情况。 一旦已经通过 UpdateLayeredWindowIndirect 复制位图,它是以该 GPU 发送直后,因此该 DWM 可以撰写在桌面上。 除了复制来回内存的零用金,强制与 CPU 同步 GPU 昂贵也是。 与典型的 CPU 绑定操作不同 GPU 操作倾向于所有被异步,执行批处理的呈现命令流时,它提供了很好的性能。 每次我们需要交叉路径替换为 CPU,它会强制刷新成批的命令并等待直到完成该 GPU,不是最佳的性能从而导致 CPU。 所有意味着您需要小心这些往返次数和频率以及所涉及的成本。 足够复杂呈现的场景是否硬件加速的性能可以很容易地超过复制该位图的成本。 手动,如果呈现不是非常昂贵,并且可以由 CPU 执行,您可能会发现对于没有硬件加速选择将最终会提供更好的性能。 这些选择 aren’t 容易进行。 某些 gpu don’t 甚至有专用的内存,而是使用减少了副本的成本的系统内存的一部分。 该问题是 GDI 和 WPF 都不向您提供一个选择。 在 GDI 的情况下您附着与 CPU。 在 WPF 的情况下您强制进入使用任何呈现方法 WPF 用途,这通常是通过 Direct3D 硬件加速。 然后来源 Direct2D。 GDI/DC 的 Direct2D 可以想象有相当几种方法可以呈现与 Direct2D 分层的窗口。 let’s 看一看一些和我指出推荐的方法,具体取决于您要使用硬件加速。 首先,您可能只需翻录出 GDI + 图形类,从 的 图 3 和替换它 Direct2D DC 与呈现目标。 这可能会使意义上,如果您有 GDI 中, 投入到很多旧版应用程序,但它 ’s 明确不最有效的解决方案。 而不是直接到 DC 呈现,Direct2D 呈现第一个对内部的 WIC 位图,然后将结果复制到 DC。 尽管比 GDI + 快,这不过涉及额外复制如果 didn’t 需要使用一个 DC 的呈现,则可以避免的。 若要用于这种方法启动通过初始化 D2D1_RENDER_TARGET_PROPERTIES 结构。 这会告诉 Direct2D 的位图以使用为其呈现目标格式。 回忆一下,它必须是 pre-multiplied 的 BGRA 像素格式。 这表示具有 D2D1_PIXEL_FORMAT 结构,定义如下: const D2D1_PIXEL_FORMAT format = const D2D1_RENDER_TARGET_PROPERTIES properties = CComPtr target; Verify(factory->CreateDCRenderTarget( const RECT rect = {0, 0, bitmap.GetWidth(), bitmap.GetHeight()}; Verify(target->BindDC(bitmap.GetDC(), &rect)); WIC 的 Direct2D CComPtr factory; CComPtr bitmap; Verify(factory->CreateBitmap( const D2D1_PIXEL_FORMAT format = const D2D1_RENDER_TARGET_PROPERTIES properties = CComPtr target; Verify(factory->CreateWicBitmapRenderTarget( CComPtr interopTarget; ID2D1GdiInteropRenderTarget 接口具有只是两种方法:GetDC 和 ReleaseDC。 若要优化使用硬件加速的位置的情况下,这些方法被限制为呈现目标已经 BeginDraw 和 EndDraw 方法调用之间使用。 GetDC 将返回该 DC 之前刷新呈现目标。 由于 interop 接口已经方法需要成对,它意义 的 图 7 所示在 c + + 类环绕。 图 7 呈现目标 DC 包装类 class RenderTargetDC { public:
} ~RenderTargetDC() { operator HDC() const { 图 8 GDI 兼容呈现方法 void Render() { const HRESULT hr = m_target->EndDraw(); if (D2DERR_RECREATE_TARGET == hr) { DXGI 是一个相对较新的子系统,居住从底层硬件抽象 Direct3D 和互操作方案提供了高性能网关的 Direct3D 下面图层上。 Direct2D 还利用简化的 Direct3D 的将来版本在移动此新的 API。 若要用于此方法首先创建 Direct3D 硬件设备。 这是表示 GPU 将执行呈现的设备。 此处为目前这必需的 Direct2D 使用 Direct3D 10.1 API: CComPtr device; Verify(D3D10CreateDevice1( 图 9 的 一个二维纹理 D3D10_TEXTURE2D_DESC description = {}; CComPtr texture; Verify(device->CreateTexture2D( 如我提到 Direct2D 都能够呈现到 DXGI API 来避免将关联到任何特定版本的 Direct3D API 通过一个 Direct3D 图面。 这意味着您需要获取 Direct3D 纹理已经基础 DXGI 表面接口传递给 Direct2D: CComPtr surface; CComPtr target; Verify(factory->CreateDxgiSurfaceRenderTarget( 所有 ’s 并没有给它。如果要呈现与硬件加速您分层的窗口使用 Direct3D 纹理。否则使用 WIC 位图。这两种方法将提供与复制的最小可能性能最佳。 请务必检查出 DirectX 博客,并在特定的、 componentization 和互操作性在 blogs.msdn.com/directx 上的 Ben Constable 已经八月 2009年文章。 |
Windows透明窗体实现总结https://blog.csdn.net/l198738655/article/details/78143996 此片文章是以前写的, 刚刚新开了博客, 就发出来跟大家分享下。 这篇文章主要讲得是vc中各种分层、透明、不规则窗口的实现, 基本囊括GDI、GDI+能使用的所有方法。 本文讲述了三种方法,其中第一种方法有两种不同效果,第三种方法有两种不同的实现方式。文中有方法使用了GDi+,关于GDI+的使用请自行查询资料,本文不进行细述。 方法一:窗体整体透明,支持子控件透明,支持OnPaint重绘。 这个方法比较简单,使用win32 Api 中SetLayeredWindowAttributes函数即可,关于该函数可查询MSDN,用这种方法有两种效果: 效果1:窗体整体透明,子控件也透明,可以实现半透明效果 //第一步要修改窗体属性,WS_EX_LAYERED支持透明 LONG lWindowStyle = ::GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED; //设置Alpha不透明度 BYTE byteAlpha = 150; //注意最后一个参数为LWA_ALPHA,第二个参数颜色掩码(透明 //色无用) SetLayeredWindowAttributes(m_hwnd, 0/any/, byteAlpha,LWA_ALPHA ) 效果2:窗体整体透明,子控件不透明,实现不规则窗体,区域透明。 首先需要一张背景位图,需要透明的地方用单一颜色填充,然后将其贴在背景上,代码如下: 第一步跟效果一中一样需修改窗体属性 ::SetWindowLong(hwnd, GWL_EXSTYLE, lWindowStyle); //将红色设为透明色, 注意透明区域鼠标并不能穿透RGB(255, 0, 0) //为透明色 //注意最后一个参数为LWA_COLORKEY,第三个参数透明度无用 ::SetLayeredWindowAttributes(hwnd, RGB(255, 0, 0), 111/any/, LWA_COLORKEY); 需要注意的是效果1和效果2可以结合起来使用,最后一个参数改成LWA_COLORKEY | LWA_ALPHA即可。使用SetLayeredWindowAttributes函数实现不规则形状简单易行,但是通常会有锯齿很难处理。 方法二:根据位图进行区域裁剪 ,关键函数CombineRgn和SetWindowRgn。 该方法跟方法一一样,需要将背景位图需要透明的地方填充为单一颜色,该方法的原理是遍历位图中的每个像素,将需要透明的像素过滤,将其他不需要透明的像素所在区域用CombineRgn函数连接起来形成一个区域,然后用SetWindowRgn将贴好背景图的窗体放进这个区域。此方法好处是可以实现镂空,即鼠标穿透透明区域。缺点是遍历每个像素对于大的位图算法时间复杂度高,效率很低。代码如下:
方法三:使用透明png贴图,并实现透明区域的透明。 此方法的优点是可以实现不规则形状贴图,鼠标能穿透透明区,并且边缘无锯齿。 该方法根据实现方式可分为两种方法 1、使用CImage(ATL和MFC中都有该类,直接用win32 api没有CImage,会麻烦点可能要用CreateFIle函数加载)绘制。 为什么我们正常的的使用CImage加载png透明区总是有白色背景呢?查了很多资料才发现这其实是微软GDI+的设计问题,PNG 图片是ARGB,使用GDI+载入图片的时候,GDI+会默认已经进行了预剩运算(PARGB),即每象素的实际值是已经和ALPHA值按比例相乘的结果,实际上它根本就没有做预乘, 在使用透明图片的象素ALPHA通道的时候,CImage 内部正是调用的AlphaBlend,没有预乘的图当作预乘的图片处理的结果就是这相当于一张和纯白背景进行了预剩, 所以图象总是出现白色背景。所以我们只需要对症下药,载入图片前与处理下即可:
最后调用CImage中Draw方法就可。 2、使用GDI+贴图,利用UpdateLayeredWindow函数实现png透明区域透明。该函数请查询MSDN。这种方法不支持子控件透明, 不支持OnPaint重绘代码如下:首先在OnInitDialog中修改窗体属性 PS:UpdateLayeredWindow函数能够让窗口透明,并且鼠标能穿过窗口。 ModifyStyleEx(0, WS_EX_LAYERED | WS_OVERLAPPED); 下面为贴图函数,注意由于不支持OnPaint,所以需重绘是手动调用贴图函数,贴图中使用到GDI+ //在OnInitDialog中初始化m_pBkImage ,m_pBkImage为 //Gdiplus::Image指针,Image::FromFile是从外面文件夹中导入png文 m_pBkImage = Image::FromFile(g_strResPath+T("main.png")); 下面为贴图函数: //调用,该方法贴的背景图不能响应WM_PAINT消息,也不能够在 //OnPaint函数中调用该绘图方法。
需要注意的是使用方法三中第二种方法虽然不会出现锯齿,但是会导致界面上的子空间全部透明,这样我们在界面上添加的控件都没用。我试过在界面上用Create方法创建及对控件重绘,但是没用。解决这个问题的方法是结合方法一中的效果二。需要两个窗口A (背景窗口),B(用于放置控件)。用方法三第二种方式实现窗口A,使用方法一种第二种效果创建B,MFC中可以在OnCtlColor函数中将背景颜色设为单一颜色,ATL中则没有OnCtlColor,最简单的做法是在OnEraseBKGND消息函数中返回TRUE(背景会变成白色),然后将背景颜色设为透明色(这时B窗体会全透明,但是其上控件不会透明),将A上所需要的控件放在B上相应位置,B窗口覆盖在A上面与其重合(由于B透明所以B上控件看着像放在A上)。在移动B窗口时同时移动A窗口。这样就能达到我们想要的效果。除了这三种方法之外,使用GDI中TransparentBlt函数也可以实现透明,该函数可以将一张有背景的贴图消除背景贴在窗体上。关于该函数使用就不再介绍了,可以查询MSDN。 |
Direct3D 12无边框半透明窗口https://zhuanlan.zhihu.com/p/127427494 语言:c++ 环境:Visual Studio 2019 组件:Direct3D 12,DirectComposition,C++/WinRT 特点:解决抖动闪烁,支持拖动、调整大小、最大化、自动排列、无边框、半透明、阴影 一、实现无边框窗口
另外无边框窗口有个令人头疼的问题:抖动闪烁(拖动左上角时,右下角不停抖动,最大化时窗口透明闪烁)许多菜鸟比如我(╥﹏╥)都会往消息处理和窗口样式方面去想,同时也会发现系统处理的窗口没有丝毫抖动。这其实是因为WM_NCPAINT和WM_NCCALCSIZE消息返回后DWM进行了默认处理,消除了抖动闪烁,同时还实现了阴影和自动排列动画。
所以处理窗口消息和样式是毫无作用的,这时就要引入Direct3D 12和DirectComposition
WinMain.cpp实现了一个无边框窗口。注释中WS_EX_NOREDIRECTIONBITMAP样式阻止绘制重定向表面(客户区),改为使用Direct3D 12接管绘制。 二、实现半透明窗口stdafx.h引用Windows.h头文件并定义了四个关于Direct3D 12的函数
|
借助DirectX的窗口半透明https://zhuanlan.zhihu.com/p/402835148 很久以前,我刚开始接触编程的时候,面对的实际上是VB这类东东——因此,我很喜欢图形界面,它们就是我最早入门时的Hello World。后来入了C++坑,用C++也做了类似的事情,借助Win API,去创建它们。在此后很长的一段时间里我告别了它们,现在因为某些原因,需要再涉及一下,因此记录一下下。 窗口里面有个老生常谈的话题——半透明,或者是,部分的半透明。 可以很轻松的创建一个全部半透明的窗口:
就可以得到这样一个窗口: 如果稍加了解,会发现实现上述效果有一些其余的方案——例如使用Direct2D或者其余的东东操作32位位图,再使用UpdateLayeredWindow 去重绘窗口,可以实现各种有意思的半透明窗口。 它们基于CPU完成大部分工作,性能堪比某聊天软件,猫猫叹气。
如果,不是很想关心Win7以及xp用户,而整个窗口的GUI是自己画的,可以来做个很有意思的事情——将这个过程丢到GPU上。 Step0. Qt? WPF? Window.UI ...?在这里希望描述的是它们的某些部分实现的一种方式,而不是使用他们。 因此不会涉及任何GUI框架(笑 Step1. 基本思想实现这个东东的第一步是处理一些小问题,我们需要把渲染到窗口的这些东东交给我们自己的程序,而不是经过一个中介。 很简单,只需要指定WS_EX_NOREDIRECTIONBITMAP 拓展样式即可。这样一来窗口上的内容就得依靠我们自己来搞了。 窗口现在完全透明了—— 与分层窗口不同的是,如果单击这些透明的地方,会发现窗口依然能有反应——也就是说,其实负责鼠标等等的东东是不知道窗口透明了的(趁它们不注意偷偷匿掉了默认绘制~),这减少了不少麻烦。 接下来我们需要一个神奇的东东: https://docs.microsoft.com/en-us/windows/win32/directcomp/directcomposition-portal 它提供了这个最终混合到桌面,呈现给用户的过程的交互部分,实际上,WPF这类框架也借助了这个东东。 我们会用它来完成最终的混合。但是它需要和D3D配合使用,因此:
因为这个新的surface完全是在GPU上完成了Alpha Blend,且,不需要关心redirection surface(因为消失啦!),因此性能会提升不少(可以在拖动窗口等等的时候看到喵~,占用比UpdateLayeredWindow要高(尤其CPU占用明显小下来了))。 干! Step2. 创建窗口按照上面描述的,创建窗口是很简单的:
只需要多指定一个拓展样式WS_EX_NOREDIRECTIONBITMAP即可。这一步可以得到前面那样的背景全透明,但是又能接受鼠标键盘事件的空窗口。 Step3. 图形API有关部分接着就可以上D2D和D3D了,在此之前,来为COM组件做个小东东:
这样就能方便处理下HRESULT的问题啦。 首先,我们要包括一些Header和库:
dwrite和本文的内容无关,但是你一定希望显示文本,因此加上它(笑。 然后,为了方便偷懒,我们用ComPtr来管理COM,例如对于接口IFuckWindows:
一切都是很轻松完成的呢。 那么就到正题了。 第零步,声明变量——(害
第一步,准备好各个鬼东东的Factory、device等等:
看起来并没有什么好说的,但是一点点,可能你注意到了,我在DEBUG下尝试开启了DirectX的调试。 接着,我们要创建交换链。需要注意的是,因为窗口的redirect surface被丢掉了,因此不能从窗口创建,要用CreateSwapChainForComposition来解决。同时,也是由于这点,我们需要手动指定大小。用GetClientRect 就能知道窗口的客户区有多大啦,假设你知道了它是winw宽winh高,那么交换链创建就可以是这样子:
然后,创建surface:
回到D2D,需要准备一个DC:
然后给它绑定一张位图,类似GDI那样:
现在就差不多了,最后,告诉DirectComposition要显示什么内容 ,以及要混合什么:
一切完成。来画点什么东东验证一下它吧! Step4. 画一些色块画色块有了这些就是很简单的啦,因为ID2D1DeviceContext继承自ID2D1RenderTarget,因此可以用dc->xxxx的方式来画画。 首先:
可以清空一下绘图区:
然后就可以开始啦,例如:
就可以绘制一个纯色的矩形。 绘制结束用EndDraw停止,D2D会把绘图命令全部提交上去。然后,通知交换链提交,让DirectComposition完成混合即可:
我画了好些颜色的色块: Step5. 改变窗口大小最后一步,我们要处理窗口大小改变的问题。很简单,只需要调用交换链的ResizeBuffers即可。但是—— 按照D3D文档的描述,这一步之前必须销毁和交换链挂钩的资源,在这里有:surface、dc、bitmap,因此,第一步,我们手动释放它们:
接着,调用ResizeBuffers:
然后重新创建这些资源:
最后,因为更改了它,因此要重新指定一次它们:
这样,一切都ok啦。 |
无边框窗口:支持调整大小和自动排列,去除非客户区,解决窗口闪烁抖动,Direct3D 12实现阴影以及半透明https://blog.csdn.net/g3030/article/details/104617476 代码思路1.首先是处理WM_NCHITTEST消息:支持调整大小和自动排列。 2.其次是WM_NCCALCSIZE消息:返回0,去除非客户区。 3.再然后经过大量尝试,解决问题:
实现阴影以及半透明用Windows.UI.Composition实现阴影以及半透明并解决初始透明。 下面先贴一段代码,还未实现半透明,等以后更。(用winrt代替了WRL) 用Direct3D 12的无边框窗口不会闪烁抖动
|
Windows下使用Direct3D和OpenGl创建带Alpha透明的窗口 |
给力嘿 |
感谢, 到时候再细看, 焦点变化时, 屏幕会闪烁或抖动, 特别是用Chrome全屏视频时, 切换到自己的应用时, 视频画面老是会闪一下, GDI上暂时没找到好的办法, 用DX好像是可以的... |
本文搜集整理了部分透明窗口实现的技术实现和文章,以供参考
窗口特性:https://docs.microsoft.com/zh-cn/windows/desktop/winmsg/window-features
分层窗口:https://docs.microsoft.com/en-us/previous-versions/ms997507(v=msdn.10)
子窗口透明
VC++实现窗口异形
关于如何实现鼠标穿透窗口和窗口半透明
鼠标穿透窗口 & 窗口渐变透明
The Desktop Window Manager
Controls and the Desktop Window Manager
使用 Windows 组合引擎实现高性能窗口分层
使用 Direct2D 绘制分层窗口
Windows透明窗体实现总结
DirectCompositionLayeredChildWindow: https://github.com/microsoft/Windows-classic-samples.git
Windows with C++ : High-Performance Window Layering Using the Windows Composition Engine
The text was updated successfully, but these errors were encountered: