在WinForms应用程序中绘制数千行的最快方法是什么

时间:2015-01-11 21:02:13

标签: c# winforms performance graphics

我有WinForms应用程序。我制作了一个用户控件,它从大约10k行的坐标绘制一个地图。实际上,并非所有线条都是直线,但是当地图完全缩小时 - 贝塞尔曲线是无关紧要的,并用直线代替。

当地图被缩放时,我的线条和曲线数量较少,因此绘图速度足够快(低于15毫秒)。但是当它完全缩小时 - 我需要绘制所有线条(因为所有线条都适合视口)。这很痛苦。在我非常快的机器上,它需要大约1000毫秒,所以在较慢的机器上它将是一个过度杀伤。

有没有一种简单的方法来加快绘图速度? 我使用Graphics对象进行绘制,并将Graphics.Scale属性设置为适合我的控件的地图。 这会减慢速度吗? 我使用Graphics.TranslateTransform()来确保整个地图都可见。 scale和translate都只在OnPaint()事件处理程序中设置一次。

然后有一个循环可以绘制大约10k行。我只是看到他们在屏幕上画画。

也许WPF容器会有帮助吗?

好吧,我可能会简化地图以合并一些行,但我想知道它是否值得付出努力。它会使代码变得非常复杂,会引入更多的计算,使用额外的内存,而且我不知道在一天结束时它是否会快得多。

顺便说一句,我测试过所有线路的处理(从一个结构转换到另一个结构,并进行一些附加计算)在我的机器上需要大约10毫秒。所以 - 单独绘图需要花费100倍的时间。

编辑: 现在这是新问题。我已经开始双重缓冲:

SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint | ControlStyles.OptimizedDoubleBuffer, true);

这是我凌乱的OnPaint()处理程序:

protected override void OnPaint(PaintEventArgs e) {
    base.OnPaint(e);
    if (Splines == null) return;

    var pens = new[] {
        new Pen(TrackColor),
        new Pen(TrackColor),
        new Pen(RoadColor),
        new Pen(RiverColor),
        new Pen(CrossColor)
    };

    var b = Splines.Bounds;
    Graphics g = e.Graphics;

    g.PageScale = _CurrentScale;
    g.TranslateTransform(-b.Left, -b.Top);
    int i = 0;
    foreach (var s in Splines) {
        g.DrawLine(pens[s.T], s.A, s.D);
        if (++i > 100) break;
        //if (s.L) g.DrawLine(pens[s.T], s.A, s.D);
        //else g.DrawBezier(pens[s.T], s.A, s.B, s.C, s.D);
    }

    foreach (var p in pens) p.Dispose();
}

如果我只从样式中删除OptimizedDoubleBuffer,请接受我的说法。当双缓冲打开时,处理程序正确执行,每个DrawLine都使用正确的参数执行。但是没有显示图形。调整大小时的CPU使用率接近零。像所有DrawLine次调用一样被忽略。这里发生了什么?

4 个答案:

答案 0 :(得分:2)

在我最近看到的相关帖子中却找不到,OP声称在切换他的控件以使用双缓冲时已经看到了大幅加速。显然,在屏幕上绘制内容会有很大的影响。

您可以尝试的另一件事是在缩小时抽取您绘制的线条中的点列表。不是每帧都进行抽取,而是每次更改缩放时只能进行一次。

答案 1 :(得分:2)

尝试双缓冲作为可能的解决方案或尝试减少行数。只有测试才能为您的应用提供答案。

Winforms Double Buffering

Double buffering with Panel

答案 2 :(得分:1)

这种可行性取决于您是否使用抗锯齿,如果物体可以旋转,如果厚度必须非常准确等等。

但是,您始终可以将所有线条绘制到位图中,然后只需重新绘制位图,除非地图数据本身已实际更改。当然,您可以为不同的缩放级别设置不同的位图,隐藏和显示它们,网格中的多个位图用于高细节等。

它绝对不是理想的,但是如果你确实需要在20ms刷新时绘制数千行,那么它可能是你唯一真正的选择。

答案 3 :(得分:0)

或者您可以在GDI +之外使用较低级别的绘图。这样的例子之一就是SlimDX。该包装器允许您从Windows控件和窗体创建DirectX设备写入。 DirectX实施后,速度最多可以提高几倍。

第二,即使在启用了DoubleBuffered的情况下在Win面板上绘制时,您始终必须使要求环境调用使用系统提供的Graphics对象实际绘制的OnPaint事件的面板无效。这种失效通常需要一个射速超过30到5的计时器,您才能流畅播放。现在,当负载增加时,由于所有事件都在单个线程下发生,因此随后的计时器事件将延迟。每次火灾后,计时器必须使线程屈服25ms左右(Windows操作系统限制)。不允许跨线程访问,使用System.Threading.Timer可以防止这种情况的发生。

有关此示例的示例,我尝试将现有的GDI代码传输到DirectX。该代码使用了许多图形属性,这些属性已合并到包装程序中,可以在GDI和DirectX上绘制。

https://drive.google.com/file/d/1DsoQl62x2YeZIKFxf252OTH4HCyEorsO/view?usp=drivesdk