使用分层窗口创建平滑的窗口边框
我们正在开发一个皮肤应用程序,其大部分窗口都有各种圆角边缘。 我使用窗口区域来定义非矩形形状,但几乎每个人都反对由此导致的锯齿状混叠,因为像素只能是完全不透明或完全透明。
我已经使用分层窗口提出了一个解决方案,但我们希望确保它能够在各种系统上运行(并希望运行良好),并且我想看看是否有人有更好的想法或方法优化我正在做的事情。 我知道分层窗口需要 win2000 或更高版本,这很好,因为出于其他原因这已经是一个要求。 从一些基本测试来看,它在 Vista 上看起来没问题,但这还不能保证。
这就是我所做的:我有一个窗口,将其称为 A,其中包含控件、文本以及包含该窗口的所有内容。 我将窗口 B 作为窗口 A 的子窗口,但它具有 WS_POPUP 样式而不是 WS_CHILD,因此它可以将自己定位在 A 的区域之外,并绘制在 A 的控件之上。 窗口 B 也具有 WS_EX_LAYERED 样式,在初始化时,我调用 UpdateLayeredWindow 带有 ULW_ALPHA 标志和带有 alpha 通道的 32 位位图的源 DC,以使其使用每像素 alpha 进行绘制。
窗口 B 的源 DC 中使用的位图几乎只是窗口边框周围的像素,我希望将其从窗口背景平滑地混合为完全透明。 我会跳过整个两个窗口方法,只使用一个分层窗口,除了当您使用 UpdateLayeredWindow 时,它是从内存中保存的缓冲区中绘制的,而不是典型的 WM_PAINT 消息等,并试图获取交互式子控件(和子窗口)能够很好地工作,这听起来像是一个非常麻烦的事情(并且可能甚至无法适用于所有情况)。
所以,它基本上是窗口 A 与所有子控件等等,窗口 B 直接浮动在它的顶部,绘制一个漂亮的平滑边框。 我通过移动窗口 B 来响应 WM_MOVE 消息等,并且我禁用了窗口 B,因此它永远无法获得焦点或输入(点击已经通过,因为它的部分不透明度为零,例如大多数其内部零件已排除在采摘范围之外)。
为了更好地展示我的意思,以下是这些作品的样子。
所以,它有效,但我不能确定这确实是最好的方法去做这个。 我有两个问题:
- 这听起来可以接受吗,还是有什么明显可怕的地方?
- 正如它目前的工作原理一样,它似乎使用了一个与窗口大小相同的离屏缓冲区(最大可达 1024x768),尽管它的很少像素具有任何非零不透明度数据 - 是否值得这样的开销以及将其切割成单独的边框块并将它们组合在一起的额外复杂性?
We are developing a skinned app with various rounded edges on most of its windows. I am using window regions to define non-rectangular shapes, but nearly everyone objects to the jagged aliasing this causes, because pixels can only be either fully opaque or fully transparent.
I've come up with a solution to this using layered windows, but we want to make sure this will run (and hopefully run well) on a variety of systems, and I want to see if anyone has any better ideas, or ways to optimize what I'm doing. I know layered windows requires win2000 or later, and that is fine, as that is already a requirement for other reasons. From some basic tests it looks ok on Vista, but that's no guarantee yet.
Here's what I do: I have a window, call it A, with controls and text and whatever comprises that window. I have window B as a child of window A, except it has the WS_POPUP style instead of WS_CHILD, so it can position itself outside A's region and is drawn on top of A's controls. Window B also has the WS_EX_LAYERED style, and on initialization, I call UpdateLayeredWindow with the ULW_ALPHA flag and a source DC with a 32bit bitmap with an alpha channel, to get it to draw with per-pixel alpha.
The bitmap used in the source DC for window B is pretty much just the pixels around the border of the window that I want to blend smoothly from the window's background into full transparency. I would skip the whole two window approach and just use a single layered window, except that when you're using UpdateLayeredWindow, it is drawn from a buffer kept in memory, in lieu of the typical WM_PAINT messages and all that, and trying to get interactive child controls (and child windows) to work well with that sounds like a remarkable hassle (and probably wouldn't even work for everything).
So, it's basically window A with all the child controls and whatever, with window B floating directly on top of it, drawing a nice smooth border. I respond to WM_MOVE messages and so on by moving window B along with it, and I have window B disabled so it can never get focus or input (clicks already go through, since parts of it that are of zero opacity, such as the majority of its inner parts, already are excluded from picking).
For kicks, here's what the pieces look like, to show what I mean a little better.
- Window A's background, with cyan used to mask fully transparent pixels.
- Window B's bitmap, with an alpha channel (not pictured of course, it's a jpg) that would blend the non-black pixels into transparency.
- Combined result
So, it works, but I can't be certain it's really the best way to do this. I have two questions:
- Does this sound acceptable, or is there anything glaringly terrible about it?
- As it currently works, it seems like it's using an off-screen buffer the size of the window (which can be up to 1024x768) even though very few pixels of it have any non-zero opacity data -- would it be worth the overhead and additional complexity of cutting it up into separate border pieces and compositing them together?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我发现一件事:拥有单独的框架片段比拥有一个巨大的框架窗口(中间有一大片空像素)的渲染速度要快得多。 我没有任何实际数字,但仅从尝试这两种方法的快速测试来看,当几个具有全帧窗口的窗口相互重叠时,会出现明显的延迟,但当它们的帧被分割成更小的组件时,它会快得多。 无论拥有自己的设备上下文的多层窗口的开销如何,都不会产生太大的影响,并且拥有大面积的像素(空白与否!)仍然会带来更多的负载。
I've found out one thing: having separate frame pieces can render significantly faster than having one single giant frame window, with a large expanse of empty pixels in the middle. I don't have any actual numbers, but just from a quick test of trying both, there was a noticeable latency when several windows with full frame windows overlapped each other, but when their frames were cut up into smaller components, it was much snappier. Whatever overhead of having multiple layered windows with their own device contexts does not contribute much, and having large expanses of pixels (blank or not!) still contributes a lot more load.
我的产品使用分层窗口来绘制附加到每个窗口的小选项卡。 我使用分层窗口来获得平滑的舍入而不会出现锯齿。 到目前为止,我遇到的唯一令人讨厌的问题是,在没有 DWM 的 Windows XP 和 Vista 上,一些 OpenGL 窗口会在分层窗口之上乱写乱画。 这是一个低级问题,微软并没有提供太多帮助。 您可以通过打开 Google Earth 并将应用程序拖动到主渲染窗口上来重现它,您的分层窗口将会消失。
My product uses layered windows to draw the little tabs I attach to each window. I used layered windows to get the smooth rounding without aliasing. The only nasty problem I have run into so far is that some OpenGL windows scribble on top of layered windows on Windows XP and Vista without DWM. Its a low level problem and Microsoft has not been terribly helpful. You can reproduce it by opening up Google Earth and dragging your application over the main rendering window, your layered window will disappear.