Possible optimization of GDI + DrawLines

I am trying to get more performance from the C # GDI + DrawLines function. When I run the code profiler, I see that almost half of the time spent in the DrawLines function prepares an array of points that should be sent to the native GDI + dll. This seems like a lot of overhead, and I wonder if anyone can come up with a more efficient way to interact with the DrawLines function than the built-in implementation of the DrawLines function in System.Drawing

Here are the native functions of 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; } 
+4
source share
2 answers

This "DrawLines" method is somewhat unsuccessful (and several other methods in .NET-GDI + -API). First of all, it only accepts an array, which means that the number of points must exactly match the size of the array; if you do not know how many points you will have in preparing the points, you need to call Array.Resize, which will copy the data. Secondly, the first action in the DrawLine method is copying points - why not transfer the original data to GDI +? In fact, data is copied twice before it gets into GDI +.
What can be done about this:

  • Use the GDI + Flat API from C # (using P / Invoke).
  • Use the (native) C ++ API] (for example, use the C ++ / CLI to interact with C #).

The first idea might look something like this:

 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 - you can access your own handles in .NET-GDI + objects using reflection (of course, this is undocumented, you need to access a private method). For a System.Drawing.Font object, it looks something like this:

 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) 
+4
source

If you are trying to improve the performance of 2D drawing operations in C # on Windows, you should consider Direct 2D # .

0
source

All Articles