2017-09-16 33 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中執行任何操作。 –