2010-11-07 25 views
4

J'essaye d'obtenir plus de performance de la fonction C# GDI + DrawLines. Lorsque j'exécute un profileur sur le code, je vois que presque la moitié du temps passé dans la fonction DrawLines prépare le tableau de points à envoyer à la DLL GDI + native. Cela semble être un gros inconvénient et je me demande si quelqu'un pourrait trouver un moyen plus efficace d'interopérer avec la fonction DrawLines qu'avec l'implémentation native de la fonction DrawLines dans System.DrawingPossibilité d'optimisation GDI + DrawLines

Voici les Systemm.Drawing natifs fonctions:

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; 
} 

Répondre

0

Si vous essayez d'améliorer la performance des opérations de dessin 2D en C# sous Windows, vous devriez envisager Direct 2D#.

+0

Merci. Direct2D est le futur chemin, mais en ce moment je suis coincé avec GDI + ce qui n'est pas vraiment mauvais. Et pourrait être encore mieux si le C# wrapper n'avait pas de surcharge, d'où ma question. – Einar

+0

Légèrement hors sujet, mais il convient de souligner que vous pouvez accéder à l'interface Direct2D en utilisant le Pack de code de l'API Window. – Samuel

4

Cette méthode "DrawLines" est quelque peu malheureuse (et plusieurs autres méthodes dans le .NET-GDI + -API). Tout d'abord, il prend juste un tableau, ce qui signifie que le nombre de points doit correspondre exactement à la taille du tableau; Si vous n'êtes pas sûr du nombre de points que vous aurez lors de la préparation des points, vous devez appeler Array.Resize qui va copier les données. Deuxièmement, la première action dans la méthode DrawLine est de copier les points - pourquoi ne pas passer les données originales à GDI +? En effet, les données sont copiées deux fois avant d'atteindre GDI +.
Que peut-on faire à ce sujet:

  • Utilisez le GDI+ Flat API de C# (en utilisant P/Invoke).
  • Utilisez l'API C++ (native)] (par exemple, utilisez C++/CLI pour interfacer avec C#).

La première idée pourrait ressembler à ceci:

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 - il est possible d'avoir accès aux poignées natives dans les objets .NET GDI + en utilisant la réflexion (bien sûr, cela est non documenté, vous devez accéder à une méthode privée). Pour l'objet System.Drawing.Font, cela ressemblerait à ceci:

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)