2017-09-16 36 views
0

鉴于Windows窗体的传统桌面应用程序,托管代码上运行在.NET Framework(的C#和VB项目组合) 3.5(由于超出此问题范围的原因无法迁移到较新的.NET), GRADUALLY如何将代码从GDI +转换为Direct2D?或者可能到Direct3D?如何从托管代码初始化的Direct2D渲染到GDI上下文旧版本的.NET框架

另一个限制是生成的应用程序在Windows 7上工作,但如果这是实现此目的唯一方法,我们将迁移到Windows 8或Windows 10。

(的动力是GDI +纹理处理的错误与Graphics.FillPath和小纹理缩放因子使用时,但我们最终想要移动到的Direct2D和Direct3D的反正。)

我们想要做什么是简单的,如果我们靶向的.NET Framework 4.0及以上版本和Windows 8+,如记录在这里:
Direct2D and GDI Interoperability Overview

不幸的是,试图以适应我们的旧目标规范这些指令又碰上了一系列路障。

第一步是使用一些托管包装来访问Direct2D。
(不知道是否Direct2D的1.0或1.1被包装/代码示例/教程在微软和其他地方的针对性。)

选项我知道:
A. 微软DirectX 9.0的托管代码(MDX)(最后一次更新2006):
我已经看到了这个长期不受支持的软件包,以及建议使用SlimDX或SharpDX(或迁移到支持的新技术,但与我们指定的旧平台不兼容)的建议。似乎不是一个好的长期方向。所以我还没有尝试过。
B. Win2D - 不支持Windows 7,也不支持.NET Framework 3.5。
C. SharpDX(开源,积极维护):
试过用这个。不幸的是,直到v3.0.0需要.NET Framework 4.0+才加入了Direct2D。所以这不是一个选择,直到我们准备对我们的应用程序进行更重大的改革。

D. SlimDX(开源,最后更新2012): 成功安装并渲染到独立的Direct2D窗口。
由于将此转换为“GDI上下文”而陷入困境,如上面链接的“互操作性概述”中所述。

C++从 “互操作性” 链接代码:

// Create a DC render target. 
D2D1_RENDER_TARGET_PROPERTIES props = D2D1::RenderTargetProperties(
    D2D1_RENDER_TARGET_TYPE_DEFAULT, 
    D2D1::PixelFormat(
     DXGI_FORMAT_B8G8R8A8_UNORM, 
     D2D1_ALPHA_MODE_IGNORE), 
    0, 
    0, 
    D2D1_RENDER_TARGET_USAGE_NONE, 
    D2D1_FEATURE_LEVEL_DEFAULT 
    ); 

hr = m_pD2DFactory->CreateDCRenderTarget(&props, &m_pDCRT); 

试图写入VB代码:

Dim factory As New Direct2D.Factory 

' --- THIS WORKS using SlimDX, SlimDX.Direct2D --- 
' (But it is not what I need; taken from SlimDX sample code) 
' Stand-alone D2D window (NOT to GDI) 
' "IntPtr handle" is "The window handle to associate with the device.". 
Dim windowProperties As New WindowRenderTargetProperties(handle, New Size(600, 600)) 
Dim target As New WindowRenderTarget(factory, windowProperties) 

' --- Hand-Translation of C++ code from "interoperability" link --- 
Dim targetProperties As New RenderTargetProperties 
targetProperties.Type = RenderTargetType.Default 
targetProperties.PixelFormat = New PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Ignore) 
' *** How invoke "ID2D1Factory::CreateDCRenderTarget"? *** 
' (There aren't many methods on SlimDX.Direct2D.Factory "factory" above, 
' so if it is possible at all, SlimDX must do this some other way.) 
' TODO 

如何前进

第一个问题:是D2D/GDI互操作性描述ribed在上面的链接可用于指定的目标平台(.NET 3.5,Windows 7)?

如果不是,那么我所尝试的是不可能的。虽然如果Windows 7出现问题,那么“Windows 10上的.NET 3.5”解决方案将值得您了解。

第二个问题假设互操作性是可能的,然后我面临着SlimDX的限制吗?或者我忽略了一些东西?我不希望将C++项目添加到此解决方案,但如果可以预编译自定义C++ dll,然后使用[除了SlimDX dll],那将是一个(几乎)可以忍受的解决方案。

而不是C++代码,手动编写托管包装来访问需要[但我无法在SlimDX中找到]来初始化D2D/GDI的互操作性?如何从上面的链接转换C++代码?

UPDATE

发现在SlimDX所需的呼叫。详情请参阅我的回答。

+0

你并不需要包括_ ** *全* _ SlimDX项目只是为了使用它。下载他们的SDK,只需添加一个对.dll文件的引用(它们以预编译的形式出现)。您可以在** Developer SDK **下的[他们的网站](https://slimdx.org/download.php)上找到它。 –

+0

@VisualVincent - 是的,那正是我正在做的。我现在已经添加了相关的VB代码,所以你可以看到我卡在哪里。 – ToolmakerSteve

+0

(我提到添加一些DLL是因为我需要的功能似乎不在SlimDX中。) – ToolmakerSteve

回答

1

在SlimDX刚发现DeviceContextRenderTarget类:

' Equivalent to "ID2D1Factory::CreateDCRenderTarget". 
    Dim target2 As New DeviceContextRenderTarget(factory, targetProperties) 

完成初始化,需要绑定该DC。

从互联互通链接C++:

HRESULT DemoApp::OnRender(const PAINTSTRUCT &ps) 
{ 

    HRESULT hr; 
    RECT rc; 

    // Get the dimensions of the client drawing area. 
    GetClientRect(m_hwnd, &rc); 

    // Create the DC render target. 
    hr = CreateDeviceResources(); 

    if (SUCCEEDED(hr)) 
    { 
     // Bind the DC to the DC render target. 
     hr = m_pDCRT->BindDC(ps.hdc, &rc); 


     // Draw with Direct2D. 

     m_pDCRT->BeginDraw(); 

     m_pDCRT->SetTransform(D2D1::Matrix3x2F::Identity()); 

     m_pDCRT->Clear(D2D1::ColorF(D2D1::ColorF::White)); 

     m_pDCRT->DrawEllipse(
      D2D1::Ellipse(
       D2D1::Point2F(150.0f, 150.0f), 
       100.0f, 
       100.0f), 
      m_pBlackBrush, 
      3.0 
      ); 

     hr = m_pDCRT->EndDraw(); 

     // Draw some GDI content. 
     if (SUCCEEDED(hr)) 
     { 
     ... 
     } 
    } 

    if (hr == D2DERR_RECREATE_TARGET) 
    { 
     hr = S_OK; 
     DiscardDeviceResources(); 
    } 

    return hr; 
} 

VB翻译:

' "canvas" is the Windows control (tested with Panel) that I wish to draw D2D in. 
Private Sub canvas_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles canvas.Paint 
    ' Render GDI content that is below D2D content 
    '... existing GDI calls ... 

    ' Render Direct2D content. 
    cDirect2DRenderer.TestRendering(e.Graphics, canvas.ClientSize) 

    ' Render GDI content that is above D2D content. 
    '... existing GDI calls ... 
End Sub 

其中采用VB类:

Imports System.Drawing 

Imports SlimDX 
Imports SlimDX.Direct2D 
Imports SlimDX.DXGI 

Public Class cDirect2DRenderer 

#Region "=== Shared ===" 
    Public Shared Sub TestRendering(gr As Graphics, canvasSize As System.Drawing.Size) 
     Dim renderer As New cDirect2DRenderer 

     ' CAUTION: After this, must call EndDraw or ReleaseHDC when done drawing. 
     Dim success As Boolean = renderer.BeginDraw(gr, canvasSize) 

     ' Render some Direct2D content. 
     success = renderer.Test_Render(success) 

     success = renderer.EndDraw(gr, success) 
     If Not success Then 
      'TODO: Log error. 
     End If 

     renderer.Dispose() : renderer = Nothing 
    End Sub 
#End Region 


#Region "=== Fields, Constructor, Dispose ===" 
    Private Ready As Boolean 
    Private _factory As New Direct2D.Factory 
    Private Target As DeviceContextRenderTarget 
    Private Bounds As Rectangle 
    Private Hdc As IntPtr 

    Public Sub New() 

    End Sub 

    Public Sub Dispose() 
     If Target IsNot Nothing Then 
      Target.Dispose() : Target = Nothing 
     End If 

     Ready = False 
    End Sub 
#End Region 


#Region "=== BeginDraw, Test_Render, EndDraw ===" 
    Public Property Factory As Direct2D.Factory 
     Get 
      Return _factory 
     End Get 
     Set(value As Direct2D.Factory) 
      If Exists(_factory) Then 
       _factory.Dispose() 
       '_factory = Nothing 
      End If 

      _factory = value 
     End Set 
    End Property 

    ' True if Ready to draw. 
    ' CAUTION: Even if returns False, Caller must call EndDraw, so that ReleaseHDC is called. 
    Public Function BeginDraw(g As Graphics, canvasSize As System.Drawing.Size) As Boolean 
     ' CAUTION: After this, must call EndDraw or ReleaseHDC when done drawing. 
     EnsureReady(g, canvasSize) 
     If Not Ready Then 
      ' Initialization failed. 
      Return False 
     End If 

     Try 
      Dim success As Boolean = True 
      Target.BeginDraw() 

      Return success 

     Catch ex As Exception 
      Return False 
     End Try 
    End Function 

    Public Function Test_Render(success As Boolean) As Boolean 
     Try 
      Target.Transform = Matrix3x2.Identity 
      Target.Clear(New Color4(Color.BlueViolet)) 
      Dim brush As Direct2D.Brush = New SolidColorBrush(Target, New Color4(Color.Black)) 
      Dim ellipse As Direct2D.Ellipse = New Ellipse() With { 
        .Center = New PointF(100, 100), 
        .RadiusX = 80, .RadiusY = 80} 
      Target.DrawEllipse(brush, ellipse) 
      Target.FillEllipse(brush, ellipse) 

     Catch ex As Exception 
      success = False 
     End Try 

     Return success 
    End Function 

    ' True if rendering succeeds. 
    ' "success" is accumulation, included in the return value. 
    Public Function EndDraw(g As Graphics, success As Boolean) As Boolean 
     ' Wrap EndDraw in Try, because "ReleaseHDC" must always be called. 
     Try 
      ' EndDraw is always called (even if "success" is already False). 
      success = success And Target.EndDraw().IsSuccess 
     Catch ex As Exception 
      success = False 
     End Try 

     ReleaseHDC(g) 
     ' TBD: This could be moved out elsewhere. 
     EnsureFactoryReleased() 

     If Not success Then 
      Trouble() 
     End If 
     Return success 
    End Function 

    ' CAUTION: Caller must call EndDraw or ReleaseHDC when done drawing. 
    Private Sub EnsureReady(g As Graphics, canvasSize As System.Drawing.Size) 
     Dim newBounds As New Rectangle(0, 0, canvasSize.Width, canvasSize.Height) 

     If Not Ready OrElse Not SameBounds(newBounds) Then 
      If Ready Then 
       Dispose() 
      End If 

      Me.Bounds = newBounds 

      Me.Ready = InitializeDevice(g) 
     End If 
    End Sub 

    ' AFTER set Me.Bounds. 
    ' CAUTION: Caller must call g.ReleaseHdc(Me.Hdc) when done drawing. 
    Private Function InitializeDevice(g As Graphics) As Boolean 
     Try 
      '' Stand-alone D2D window (NOT to GDI) 
      ' ...width As Integer, height As Integer 
      'Dim windowProperties As New WindowRenderTargetProperties(handle, New Size(600, 600)) 
      'Dim target1 As New WindowRenderTarget(factory, windowProperties) 

      Dim targetProperties As New RenderTargetProperties 
      targetProperties.Type = RenderTargetType.Default 
      targetProperties.PixelFormat = New PixelFormat(Format.B8G8R8A8_UNorm, AlphaMode.Ignore) 
      ' Equivalent to "ID2D1Factory::CreateDCRenderTarget". 
      Me.Target = New DeviceContextRenderTarget(Me.Factory, targetProperties) 

      ' CAUTION: Caller must call g.ReleaseHdc(Me.Hdc) when done drawing. 
      Me.Hdc = g.GetHdc() 
      Try 
       'TestStr = Me.Hdc.ToString() 
       Dim result As SlimDX.Result = Target.BindDeviceContext(Me.Hdc, Me.Bounds) 

       If Not result.IsSuccess Then 
        ReleaseHDC(g) 
       End If 
       Return result.IsSuccess 

      Catch ex As Exception 
       ReleaseHDC(g) 
       Return False 
      End Try 

     Catch ex As Exception 
      Return False 
     End Try 
    End Function 

    Private Sub ReleaseHDC(g As Graphics) 
     Try 
      g.ReleaseHdc(Me.Hdc) 
     Finally 
      Me.Hdc = Nothing 
     End Try 
    End Sub 

    Private Sub EnsureFactoryReleased() 
     Me.Factory = Nothing 
    End Sub 

    Private Function SameBounds(newBounds As Rectangle) As Boolean 
     ' TBD: Does Equals do what we need? 
     Return (newBounds.Equals(Me.Bounds)) 
    End Function 
#End Region 

End Class 
+1

CreateDCRenderTarget中的'DC'代表'DeciveContext',所以它应该正是你所需要的。 AFAIK SlimDX是DirectX的完整包装器,所以您可以在C/C++中执行任何操作,您也可以使用SlimDX在.NET中执行任何操作。 –