渲染半透明/透明叠加

Alp*_*lta 8 .net c# gdi+ overlay .net-2.0

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

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


解决方案1

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

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

这个解决方案的问题是它不支持透明度; 它只会"淘汰"的颜色完全一样的TransparencyKey,并且与哪怕是一丁点差,任何事情都会被显示投掷屏幕窗外在具有半透明物体的想法.


解决方案2

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

它的功能很好,不幸的是有一些问题.

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

其次; 使用半透明刷子,这种方法GDI +非常慢,FillRectangle我真的不能责怪它,但我要求它做得很快; GDI +显然无法满足的要求.


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

Fra*_*man 3

该解决方案不在您的列表中。解决方案 #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);
    }
Run Code Online (Sandbox Code Playgroud)

位图是 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);
        }
Run Code Online (Sandbox Code Playgroud)

要在内部使用我们的 GdiBuffer:

_gdiBuffer = new GdiBitmap(_bufferSize);
_graphics = Graphics.FromHdc(_gdiBuffer.DeviceContext.DangerousGetHandle());
Run Code Online (Sandbox Code Playgroud)

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