2010-11-07 86 views
4

我想從c#GDI + DrawLines函數中獲得更多的性能。當我在代碼上運行一個分析器時,我發現幾乎有一半的時間花在DrawLines函數中,準備將點數組發送到本地GDI + dll。這似乎是一個很大的開銷,我想知道是否有人可以想出一個更有效的方式與DrawLines函數進行交互,而不是使用System.Drawing中的DrawLines函數的本地實現。可能的GDI + DrawLines優化思考

以下是本機Systemm.Drawing功能:

public void DrawLines(Pen pen, PointF[] points) 
{ 
    if (pen == null) 
    { 
     throw new ArgumentNullException("pen"); 
    } 
    if (points == null) 
    { 
     throw new ArgumentNullException("points"); 
    } 
    IntPtr handle = SafeNativeMethods.Gdip.ConvertPointToMemory(points); 
    int status = SafeNativeMethods.Gdip.GdipDrawLines(new HandleRef(this,this.NativeGraphics), new HandleRef(pen, pen.NativePen), new HandleRef(this, handle),  points.Length); 
Marshal.FreeHGlobal(handle); 
    this.CheckErrorStatus(status); 

}

internal static IntPtr ConvertPointToMemory(PointF[] points) 
{ 
    if (points == null) 
    { 
     throw new ArgumentNullException("points"); 
    } 
    int num2 = Marshal.SizeOf(new GPPOINTF().GetType()); 
    int length = points.Length; 
    IntPtr ptr = Marshal.AllocHGlobal((int) (length * num2)); 
    for (int i = 0; i < length; i++) 
    { 
     Marshal.StructureToPtr(new GPPOINTF(points[i]), (IntPtr) (((long) ptr) + (i * num2)), false); 
    } 
    return ptr; 
} 

回答

0

如果你正在試圖改善在Windows上的C#2D繪圖操作的性能,你應該考慮Direct 2D#

+0

謝謝。 Direct2D是未來的道路,但是現在我被困在GDI +中,這並不是那麼糟糕。如果c#wrapper沒有這樣的開銷,那麼可能會更好,因此我的問題。 – Einar 2010-11-07 01:25:15

+0

稍微偏離主題,但值得指出的是,您可以使用Window API代碼包訪問Direct2D界面。 – Samuel 2010-11-07 01:32:45

4

這種方法「DrawLines」有點不幸(在.NET-GDI + -API中還有其他一些方法)。首先,它只需要一個數組,這意味着點的數量必須完全匹配數組的大小;如果您不確定在準備點時您將擁有多少點,則必須調用將複製數據的Array.Resize。其次,DrawLine方法的第一步是複製點 - 爲什麼不將原始數據傳遞給GDI +?實際上,數據在到達GDI +之前被複制兩次。
用什麼辦法來實現:

  • 從使用C#的GDI+ Flat API(使用P/Invoke)。
  • 使用(native)C++ API](例如使用C++/CLI與C#進行接口)。

第一個想法可能是這個樣子:

public void DrawLines(System.Drawing.Color color, ref System.Drawing.Point[] points, int pointsCount) 
    { 
     IntPtr pen = IntPtr.Zero; 
     int status = GdipCreatePen1(
           color.ToArgb(), 
           1, 
           (int)GraphicsUnit.World, 
           out pen); 
     unsafe 
     { 
      fixed (Point* pointerPoints = points) 
      { 
       status = GdipDrawLinesI(new HandleRef(this, this.handleGraphics), new HandleRef(this, pen), (IntPtr)pointerPoints, pointsCount); 
      } 
     } 

     status = GdipDeletePen(new HandleRef(this, pen)); 
    } 

    [DllImport(GDIPlusDll, SetLastError = true, ExactSpelling = true, CharSet = System.Runtime.InteropServices.CharSet.Unicode)] 
    private static extern int GdipDrawLinesI(HandleRef graphics, HandleRef pen, IntPtr points, int count);   
    [DllImport(GDIPlusDll, SetLastError = true, ExactSpelling = true)] 
    private static extern int GdipDeletePen(HandleRef pen); 
    [DllImport(GDIPlusDll, SetLastError = true, ExactSpelling = true)] 
    private static extern int GdipCreatePen1(int argb, float width, int unit, out IntPtr pen); 

BTW - 這是可能通過使用反射+對象可以訪問本機的手柄在.NET-GDI(當然,這是無證,你需要訪問一個私有方法)。對於System.Drawing.Font對象,它看起來像這樣:

Type type = typeof(System.Drawing.Font); 
System.Reflection.PropertyInfo propInfoNativeFontHandle = type.GetProperty("NativeFont", System.Reflection.BindingFlags.GetProperty | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); 
System.Drawing.Font font = ... 
IntPtr nativeHandle = propInfoNativeFontHandle.GetValue(font, null)