渲染半透明/透明叠加

本文关键字:透明 叠加 半透明 | 更新日期: 2023-09-27 18:08:52

我需要一个快速的方法来绘制一个覆盖在屏幕上的透明度支持。我做了大量的搜索,发现了一个潜在的解决方案(它有自己的问题)和另一个不符合我要求的解决方案;特别是透明度支持。

我将从后者开始,然后再谈到前者。


解决方案1

使用无边框的表单和透明键,这是我发现的最推荐的解决方案之一,也是最没有帮助的。

这个解决方案的工作原理,有一个新的形式,有它的无边界,设置背景类似Colour.WhiteTransparencyKey设置为相同的颜色,有它的全屏和顶部,也许设置一些其他的选项,使其行为不可见的鼠标和键盘。

这个解决方案的问题是它不支持透明度;它只会"敲掉"颜色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字段是我们实际绘制的位置