C# 绘制之字形线比绘制直线慢得多

C# 绘制之字形线比绘制直线慢得多,c#,gdi+,C#,Gdi+,在使用自写图形控件时,我注意到,与显示干净数据时相比,在显示嘈杂数据时,图形的绘制速度要慢得多。 我进一步深入研究并缩小了问题的范围,使之达到了最小的差异:用不同的Y值绘制相同数量的线,而不是用相同的Y值绘制线 例如,我将以下测试放在一起。我生成点列表,一个具有随机Y值,一个具有相同Y值,一个具有Z字形Y模式 private List<PointF> GenerateRandom(int n, int width, int height) { //Generate rando

在使用自写图形控件时,我注意到,与显示干净数据时相比,在显示嘈杂数据时,图形的绘制速度要慢得多。
我进一步深入研究并缩小了问题的范围,使之达到了最小的差异:用不同的Y值绘制相同数量的线,而不是用相同的Y值绘制线

例如,我将以下测试放在一起。我生成点列表,一个具有随机Y值,一个具有相同Y值,一个具有Z字形Y模式

private List<PointF> GenerateRandom(int n, int width, int height)
{
    //Generate random pattern
    Random rnd = new Random();
    float stepwidth = Convert.ToSingle(width / n);
    float mid = Convert.ToSingle(height / 2);
    float lastx = 0;
    float lasty = mid;
    List<PointF> res = new List<PointF>();
    res.Add(new PointF(lastx, lasty));
    for (int i = 1; i <= n; i++)
    {
        var x = stepwidth * i;
        var y = Convert.ToSingle(height * rnd.NextDouble());
        res.Add(new PointF(x, y));
    }
    return res;
}
private List<PointF> GenerateUnity(int n, int width, int height)
{
    //Generate points along a simple line
    float stepwidth = Convert.ToSingle(width / n);
    float mid = Convert.ToSingle(height / 2);
    float lastx = 0;
    float lasty = mid;
    List<PointF> res = new List<PointF>();
    res.Add(new PointF(lastx, lasty));
    for (int i = 1; i <= n; i++)
    {
        var x = stepwidth * i;
        var y = mid;
        res.Add(new PointF(x, y));
    }
    return res;
}
private List<PointF> GenerateZigZag(int n, int width, int height)
{
    //Generate an Up/Down List
    float stepwidth = Convert.ToSingle(width / n);
    float mid = Convert.ToSingle(height / 2);
    float lastx = 0;
    float lasty = mid;
    List<PointF> res = new List<PointF>();
    res.Add(new PointF(lastx, lasty));
    var state = false;
    for (int i = 1; i <= n; i++)
    {
        var x = stepwidth * i;
        var y = mid - (state ? 50 : -50);
        res.Add(new PointF(x, y));
        state = !state;
    }
    return res;
}
因此,它似乎变得越来越糟糕,绘制的线条变化越大,这也是我在一开始提到的控制绘画中遇到的真实效果

因此,我的问题如下:

  • 为什么会有如此残酷的差别,应该划出哪条线
  • 如何提高噪声数据的绘制速度

  • 我想到三个原因:

    • 线条长度:根据实际数量,倾斜线条可能会更长一些像素或更长一些,甚至更长一些。看看你的代码,我怀疑是后者

    • 算法:绘制斜线确实需要一些算法才能找到下一个像素。即使是快速绘制例程也需要进行一些计算,而不是垂直或水平线,它们直接穿过像素阵列

    • 消除混叠:除非完全关闭消除混叠(产生所有难看的后果),否则要绘制的像素数也将增加约2-3倍,因为还必须计算和绘制中心线上方和下方的所有消除混叠像素。别忘了计算它们的颜色


    后一部分的补救措施显然是关闭抗锯齿,但其他问题只是事情的现状。因此,最好不要担心并为快速的直线感到高兴:-)

    如果你有很多直线,或者你的直线可能很长(是屏幕大小的几倍),或者如果你有很多接近0像素的直线,你必须编写代码来减少无用的直线绘制

    好吧,这里有一些想法:

    • 如果你在同一个x上写了很多行,那么你可以用在该x上最小和最大y之间的一行来替换它们
    • 如果你的线超出了屏幕边界,你应该剪掉它们
    • 如果一条线完全位于可见区域之外,则应跳过它
    • 如果一行的长度为0,则不应写入该行
    • 如果一行只有一个像素长度,则应仅写入该像素
    显然,好处很大程度上取决于你画了多少条线。。。另一种选择可能不会给出完全相同的结果

    实际上,如果你在屏幕上画一张图表,那么如果你只显示有用的信息,在现代硬件上应该会非常快

    如果您使用样式或颜色,那么优化数据的显示可能就不那么简单了


    或者,它们是一些为显示大数据而优化的图表组件。。。好的通常很贵,但可能仍然值得。通常情况下,试验是可用的,因此您可以很好地了解可以提高性能的程度,然后决定如何做。

    忽略这样一个事实,即贝克马克总是比错误更难正确:1)根据y变化,您的线条可能会更长。您应该从结果中消除这种差异。2) 如果直线没有更快,我会感到惊讶,因为这意味着gdi例程无法识别它们,并且错过了一个优化的好机会。即使是快速直线算法也必须比水平/垂直直线算法慢,因为你根本不需要任何算法,也不需要保持坡度,也不需要对步骤进行平滑。3)你可能需要测试各种平滑模式。使用antaliasing时,即使是轻微的倾斜也会导致必须在中心线上方和下方绘制的像素数增加2-3倍。@TaW该死,我认为你的第一点是正确的,而且现在你指出了,这一点非常明显。如果我除以平均线长度,随机时间和锯齿时间收敛。直道实际上是最慢的,但这可能只是意味着其他一些开销。所以这就是原因,如果有办法的话,我们得想办法解决它。但优化线绘制是另一个问题。你能补充一个答案吗?谢谢你的意见,菲尔。在优化绘图过程以减少不需要的绘图操作方面,您有很多优点。我会调查的。在大多数情况下,这很好,因为我的组件不用于实时数据。最近,我把它与数字读数一起使用,注意到在嘈杂的时段,速度会急剧下降,这对我来说很奇怪。该图表通常针对具有一些奇怪的自定义功能的其他内容进行优化;)
    private void DoTheTest()
    {
        Bitmap bmp = new Bitmap(970, 512);
        var random = GenerateRandom(2500, bmp.Width, bmp.Height).ToArray();
        var unity = GenerateUnity(2500, bmp.Width, bmp.Height).ToArray();
        var ZigZag = GenerateZigZag(2500, bmp.Width, bmp.Height).ToArray();
    
        using (Graphics g = Graphics.FromImage(bmp))
        {
            var tUnity = BenchmarkDraw(g, 200, unity);
            var tRandom = BenchmarkDraw(g, 200, random);
            var tZigZag = BenchmarkDraw(g, 200, ZigZag);
            MessageBox.Show(tUnity.ToString() + "\r\n" + tRandom.ToString() + "\r\n" + tZigZag.ToString());
        }
    }
    private double BenchmarkDraw(Graphics g, int n, PointF[] Points)
    {
        var Times = new List<double>();
        for (int i = 1; i <= n; i++)
        {
            g.Clear(Color.White);
            System.DateTime d3 = DateTime.Now;
            DrawLines(g, Points);
            System.DateTime d4 = DateTime.Now;
            Times.Add((d4 - d3).TotalMilliseconds);
        }
        return Times.Average();
    }
    private void DrawLines(Graphics g, PointF[] Points)
    {
        g.DrawLines(Pens.Black, Points);
    }
    
    Straight Line: 0.095 ms
    Zig-Zag Pattern: 3.24 ms
    Random Pattern: 5.47 ms