c# - 可能的GDI+ DrawLines Optimization思考

标签 c# optimization interop system.drawing

我正在尝试从 c# GDI+ DrawLines 函数中获得更多性能。当我在代码上运行探查器时,我发现在 DrawLines 函数上花费的时间几乎有一半是在准备要发送到 native GDI+ dll 的点数组。这似乎是一个很大的开销,我想知道是否有人能想出一种比 System.Drawing 中 DrawLines 函数的 native 实现更有效的方式与 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;
}

最佳答案

这种方法“DrawLines”有点不幸(以及 .NET-GDI+-API 中的其他几种方法)。首先,它只需要一个数组,这意味着点的数量必须与数组的大小完全匹配;如果您不确定在准备点时会有多少点,则必须调用 Array.Resize 来复制数据。其次,DrawLine 方法中的第一个 Action 是复制点 - 为什么不将原始数据传递给 GDI+?实际上,数据在到达 GDI+ 之前被复制了两次。
可以做些什么:

  • 使用GDI+ Flat API来自 C#(使用 P/Invoke)。
  • 使用(原生)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);

顺便说一句 - 可以通过使用反射访问 .NET-GDI+ 对象中的 native 句柄(当然,这是未记录的,您需要访问私有(private)方法)。对于 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)

关于c# - 可能的GDI+ DrawLines Optimization思考,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4116016/

相关文章:

c# - 如何读取带有自定义扩展名的文本框的文件

c# - Linq to sql - 左外连接

c++ - libc++中短字符串优化的机制是什么?

c++ - 组合整数和 float : performance considerations

c# - 在C++中定义一个需要在C#和C++中实现的接口(interface)

java - 通过派生类访问Java中定义的静态内部类

c# - 使用 bool 结果,添加到枚举以作为参数传递?

c# - 从链表中删除

optimization - 如何在 Golang 中获取指向任何数据类型值的字节指针?

c - string.withCString 和 UnsafeMutablePointer(变异 : cstring) wrapped into a function