渲染半透明/透明叠加
本文关键字:透明 叠加 半透明 | 更新日期: 2023-09-27 18:08:52
我需要一个快速的方法来绘制一个覆盖在屏幕上的透明度支持。我做了大量的搜索,发现了一个潜在的解决方案(它有自己的问题)和另一个不符合我要求的解决方案;特别是透明度支持。
我将从后者开始,然后再谈到前者。
解决方案1
使用无边框的表单和透明键,这是我发现的最推荐的解决方案之一,也是最没有帮助的。
这个解决方案的工作原理,有一个新的形式,有它的无边界,设置背景类似Colour.White
和TransparencyKey
设置为相同的颜色,有它的全屏和顶部,也许设置一些其他的选项,使其行为不可见的鼠标和键盘。
这个解决方案的问题是它不支持透明度;它只会"敲掉"颜色与TransparencyKey
完全相同的,因此即使是最轻微的差异也会显示出在屏幕上有半透明物体的想法。
解决方案2
使用p/Invoke和GDI+得到(GetDC
&Graphics.FromHdc
),实际绘制到,然后释放(ReleaseDC
)桌面。
对于它所做的,它工作得很好,不幸的是,然而有一些问题。
首先;调用它一次将很明显只绘制它一次,因此,如果屏幕刷新后,它会消失,我不知道如何解决这个问题,如果这是最好的解决方案,我肯定需要帮助这个问题。
其次;GDI+使用半透明笔刷和FillRectangle
的方法非常慢,老实说我不能责怪它,但是我要求它完成得相当快;这是GDI+显然无法满足的要求。
结论;我需要一种绘制覆盖在屏幕上的方法,具有透明度的支持,如果第二种方法是我应该使用的方法,我怎么能用一个半透明的画笔画得足够快,这不是一个问题,我如何让它更新,使它不会在屏幕刷新后消失,如果它不是我应该使用的方法,请指定我应该使用什么方法
解决方案不在您的列表中。解决方案#3是使用带有alpha通道的位图的UpdateLayeredWindow。当更新位图时,你必须只绘制需要更新的区域,并使用最快的位图格式(预乘法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);
}
Bitmap是GDI+位图。这意味着我们_deviceContext现在既表示GDI+位图,也表示我们可以在GDI中使用的东西。
当需要更新屏幕时,我们将一个Form传递到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字段是我们实际绘制的位置