zoukankan      html  css  js  c++  java
  • 在 WinForm 中使用 Direct2D

    在 C# 的 WinForm 应用中,界面的绘制使用的是 GDI+。不过在一些特别的应用中,可能需要用硬件加速来提高绘制的效率。下面就来介绍两种在 WinForm 应用中嵌入 Direct2D 的方法。

    这里所谓的“嵌入”,指的是只有窗口的某一部分应用 Direct2D 绘制(用一些控件承载),而不是整个窗口都使用 Direct2D 绘制。这是一种混合方案,需要用硬件加速的部分由自己来绘制,其它部分仍然可以使用现有的 WinForm 技术。

    至于 Direct2D 的类库,我仍然使用 SharpDX 类库,使用 SharpDX.Windows.RenderControl 控件承载 Direct2D 渲染。

    一、使用 HwndRenderTarget

    HwndRenderTarget(ID2D1HwndRenderTarget interface),在 SharpDX 中对应的类是 WindowRenderTarget,是将窗口句柄(hwnd)作为渲染目标的类,利用它可以非常容易的在窗口中嵌入 Direct2D 渲染。

    它的用法非常简单,只要先创建一个 Direct2D 工厂(SharpDX.Direct2D1.Factory),接下来直接创建 WindowRenderTarget 实例,然后就可以使用了。其核心代码如下所示:

    // 创建 Direct2D 单线程工厂。
    Factory factory = new Factory(FactoryType.SingleThreaded);
    // 渲染参数。
    RenderTargetProperties renderProps = new RenderTargetProperties
    {
        PixelFormat = D2PixelFormat,
        Usage = RenderTargetUsage.None,
        Type = RenderTargetType.Default
    };
    // 渲染目标属性。
    HwndRenderTargetProperties hwndProps = new HwndRenderTargetProperties()
    {
        // 承载控件的句柄。
        Hwnd = hwndRenderControl.Handle,
        // 控件的尺寸。
        PixelSize = new Size2(hwndRenderControl.ClientSize.Width, hwndRenderControl.ClientSize.Height),
        PresentOptions = PresentOptions.None
    };
    // 渲染目标。
    hwndRenderTarget = new WindowRenderTarget(factory, renderProps, hwndProps)
    {
        AntialiasMode = AntialiasMode.PerPrimitive
    };

    一般的用法,就是在控件的 Paint 事件中,调用 hwndRenderTarget 的相关方法进行绘制。需要特别注意的是,如果控件的大小发生了改变,必须调用 WindowRenderTarget.Resize 方法,重新调整渲染目标的尺寸才可以,否则会导致绘制结果被拉伸,引起失真。

    这个方法的优点就是非常简单易用,而且基本上只要操作系统是 Windows 7 及更高版本,都可以正常绘制(在第 8 行设置 Type = RenderTargetType.Default,会根据情况自动选择硬件渲染或软件渲染),适用范围很广。

    其缺点首先是不能在 Windows 8 的 Store app 中使用,其次是与 Direct2D 的一些高级功能不能很好的结合。Direct2D 的很多高级功能,如渲染特效,都是需要与 DeviceContext 结合使用的,而 HwndRenderTarget 不能直接使用 DeviceContext 的渲染结果。

    二、使用 DeviceContext

    DeviceContext 则是一个比较“万能”的类,它可以将结果绘制到任意的 Image 上,在离线渲染与多线程渲染中是非常有用的。

    使用 DeviceContext 来绘制 Direct2D 的做法则要复杂很多,由于 DeviceContext 并不能直接将结果渲染到窗口句柄上,因此需要在 DeviceContext 和 hwnd 之间建立起联系,这里使用的是 DXGI 的 SwapChain

    大致的步骤,是先利用 DeviceContext,将结果绘制到一块缓冲区中(BackBuffer 后台缓冲区),然后由 SwapChain 将后台缓冲区的内容,呈现到 hwnd 上,完成一次绘制。

    创建时,需要创建 Direct3D Device、DXGI Device 和 Diect2D Device,这样才能创建 DeviceContext。接着再创建 SwapChain 和相应的 Surface 作为缓冲区,才能正常使用。相应的代码如下所示,由于会有很多重名类,因此我用 using 语句定义了很多类型别名,代码看起来会乱一些:

    // 创建 Dierect3D 设备。
    D3D11Device d3DDevice = new D3D11Device(DriverType.Hardware, DeviceCreationFlags.BgraSupport);
    DXGIDevice dxgiDevice = d3DDevice.QueryInterface<D3D11Device1>().QueryInterface<DXGIDevice>();
    // 创建 Direct2D 设备和工厂。
    D2D1Device d2DDevice = new D2D1Device(dxgiDevice);
    this.deviceContext = new DeviceContext(d2DDevice, DeviceContextOptions.None);
     
    // 创建 DXGI SwapChain。
    SwapChainDescription swapChainDesc = new SwapChainDescription()
    {
        BufferCount = 1,
        Usage = Usage.RenderTargetOutput,
        OutputHandle = dcRenderControl.Handle,
        IsWindowed = true,
        // 这里宽度和高度都是 0,表示自动获取。
        ModeDescription = new ModeDescription(0, 0, new Rational(60, 1), Format),
        SampleDescription = new SampleDescription(1, 0),
        SwapEffect = SwapEffect.Discard
    };
    this.swapChain = new SwapChain(dxgiDevice.GetParent<Adapter>().GetParent<DXGIFactory>(),
        d3DDevice, swapChainDesc);
    // 创建 BackBuffer。
    this.backBuffer = Surface.FromSwapChain(this.swapChain, 0);
    // 从 BackBuffer 创建 DeviceContext 可用的目标。
    this.targetBitmap = new Bitmap1(this.deviceContext, backBuffer);
    this.deviceContext.Target = targetBitmap;

    绘制同样是在控件的 Paint 事件中绘制的,但要记得在最后调用 swapChain.Present 方法,令 SwapChain 将结果呈现在 hwnd 中。

    在改变控件大小时,也要复杂很多。因为在调用 SwapChain 的 ResizeBuffers 方法之前,需要先将与 SwapChain 相关的资源全部释放掉,才能调整缓冲区的尺寸,最后还要重建相关的资源。

    使用 DiviceContext 的优点很多,包括:

    • 可以用于 Windows Store apps。
    • DeviceContext 可以绘制到其它目标,只要简单的改变 Target 属性即可。
    • 可以创建 Direct2D 特效。
    • 可以使用多个 DeviceContext 来进行多线程绘制,可以参见这里

    在下面的 Demo 中,我也简单的演示了其它 DeviceContext 的绘制结果,可以直接被绘制到界面中。

    该方法的缺点,就是创建略微复杂,而且需要电脑在一定程度上支持 Direct3D 才可以(具体要支持到什么程度,还不清楚~)。

    关于 Direct2D 特效的应用,可以参见《C# 使用 Direct2D 实现斜角效果》。Direct2D 在 WinForm 中的实际应用,可以参见我的拼图游戏

    所有源代码和用到的类库都可以在这里下载。

    原文链接:在 WinForm 中使用 Direct2D

  • 相关阅读:
    【转】JSON.parse() Unexpected token i in JSON at position 2 报错问题
    修改json对象的每一个值
    浏览器各个版本和系统(chrome/safari/edge/qq/360)
    数据库书籍推荐排行榜
    git-将dev代码合并到test
    npm install报错
    slice()和splice()区别
    在Eclipse中使用JUnit4进行单元测试(初级篇)
    [转载]Jmeter那点事·ForEach和If控制器
    java语言写文件内容
  • 原文地址:https://www.cnblogs.com/rainbow70626/p/5475415.html
Copyright © 2011-2022 走看看