渲染半透明/透明叠加

我需要一种快速的方法来在支持透明度的屏幕上绘制叠加层。 我做了很多搜索,找到了一个可能的解决方案(有自己的问题)和另一个不符合我要求的解决方案; 特别是透明支持。

我将从后者开始,然后触摸前者。


解决方案1

使用带有TransparencyKey的无边框表单,这是我发现的最有用的解决方案之一。

这个解决方案的工作原理是使用一个新的Form,让它无边框,将背景设置为类似Colour.White ,并将TransparencyKey设置为相同的颜色,将其设置为全屏和最顶层,并可能设置一些其他选项以使其不可见鼠标和键盘。

该解决方案的问题在于它不支持透明度; 它只会“剔除”与TransparencyKey 完全相同的颜色,因此,即使是最微小的差异,也会显示出在屏幕外面有半透明物体的想法。


解决方案2

使用P / Invoke和GDI +获取( GetDCGraphics.FromHdc ),实际绘制,然后释放( ReleaseDC )桌面。

它的function很好,不幸的是,但是存在一些问题。

首先; 调用它一次显然只会画一次,因此如果画面完全刷新后它会消失,我不明白如何解决这个问题,如果这是最好的解决方案,我肯定需要帮助这个问题。

第二; 使用半透明的Brush和FillRectangle这种方法GDI +非常慢,我老实说不能怪它,但我要求它做得很快; GDI +显然无法满足的要求。


结论; 我需要一种在具有透明度支持的屏幕上绘制叠加层的方法,如果第二种方法是我应该使用的方法,我怎么能用半透明刷子绘制得足够快以至于它不是问题我怎么能让它更新呢屏幕刷新后它不会消失,如果不是我应该使用的方法,请指定我应该使用的方法。

解决方案不在您的列表中。 解决方案#3是将UpdateLayeredWindow与带有alpha通道的位图一起使用。 更新位图时,必须仅绘制需要更新的区域,并使用最快的位图格式(预乘ARBG)。 这是我使用解决方案#2创建的一些图形的示例,它看起来足够快: 半透明时钟和计算器

这里有一些更多的细节。 我们有一个名为GdiBuffer的类,它包含一个GDI句柄字段和一个DC句柄字段(都封装在类,GdiHandle和DCHandle中)。 它按以下方式初始化:

  public GdiBitmap(int width, int height) { using (Bitmap bitmap = new Bitmap(width, height)) Initialize(bitmap); } private void Initialize(Bitmap bitmap) { _size = bitmap.Size; _handle = new GdiHandle(bitmap.GetHbitmap(), true); _deviceContext = UnsafeNativeMethods.CreateCompatibleDC(IntPtr.Zero); UnsafeNativeMethods.SelectObject(_deviceContext, _handle); } 

位图是GDI +位图。 这意味着我们_deviceContext现在表示GDI +位图,以及我们可以在GDI中使用的东西。

当需要更新屏幕时,我们将表单传递给GdiBuffer中可以更新屏幕的方法:

  if (!form.Visible) return false; IntPtr formHandle = form.Handle; if (formHandle == IntPtr.Zero) return false; Check.Argument(opacity >= 0 && opacity <= 1, "opacity"); // the future bounds was stored if the TranslucentForm Bounds property // changed at any time. the true update to the window bounds only // happens here, in the UpdateLayeredWindow call bool futureBoundsSet = form.FutureBoundsSet; Rect bounds = form.GetFutureBounds(); SIZE newSize = new SIZE(bounds.Width, bounds.Height); POINT newPosition = new POINT(bounds.Left, bounds.Top); POINT pointSource = new POINT(); BLENDFUNCTION blend = new BLENDFUNCTION((byte)(opacity * 255)); IntPtr screenDC = UnsafeNativeMethods.GetDC(IntPtr.Zero); bool result; try { result = UnsafeNativeMethods.UpdateLayeredWindow(formHandle, screenDC, ref newPosition, ref newSize, _deviceContext, ref pointSource, 0, ref blend, UnsafeNativeMethods.ULW_ALPHA); } finally { if (screenDC != IntPtr.Zero) UnsafeNativeMethods.ReleaseDC(IntPtr.Zero, screenDC); } 

要在内部使用我们的GdiBuffer:

 _gdiBuffer = new GdiBitmap(_bufferSize); _graphics = Graphics.FromHdc(_gdiBuffer.DeviceContext.DangerousGetHandle()); 

_graphics字段是我们实际绘制的地方。