渲染半透明/透明叠加

时间:2014-06-17 10:50:25

标签: c# .net overlay .net-2.0 gdi+

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

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


解决方案1 ​​

使用带有TransparencyKey的无边框表格,这是我发现的最有用的解决方案之一,而且帮助最少。

此解决方案通过使用新表单,使其无边框,将背景设置为Colour.White并将TransparencyKey设置为相同颜色,将其设置为全屏和最顶层,并且可能设置其他一些选项让它对鼠标和键盘不起作用。

此解决方案的问题在于它不支持透明度;它只会'淘汰'#39;颜色TransparencyKey完全相同,因此,即使是最微小的差异,也会显示出在屏幕上出现半透明物体的想法。


解决方案2

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

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

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

其次;使用半透明刷子和FillRectangle这种方法GDI +非常慢,老实说我不能责怪它,但是我要求它很快完成; GDI +显然无法满足的要求。


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

1 个答案:

答案 0 :(得分:2)

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

以下是一些更多细节。我们有一个名为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是GD​​I +位图。这意味着我们_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字段是我们实际绘制的地方。