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

时间:2016-11-15 20:24:53

标签: c# gdi+

在使用自写图形控件时,我注意到图形绘制在显示噪声数据时比显示干净数据要慢得多。
我进一步挖掘并将问题缩小到其最小差异:绘制相同数量的具有不同Y值的线与具有相同Y值的绘制线。

例如,我将以下测试放在一起。我生成点列表,一个具有随机Y值,一个具有相同的Y,一个具有Zig-Zag 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;
}

我现在绘制几个点列表并比较它需要多长时间:

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

所以看起来越来越糟糕,画线中的变化越多,这也是我在开头提到的控制画中遇到的真实效果。

我的问题如下:

  1. 为什么会产生如此残酷的差异,哪些线条会被绘制?
  2. 如何提高噪声数据的绘制速度?

2 个答案:

答案 0 :(得分:3)

有三个理由浮现在脑海中:

  • 线长:根据实际数字,斜线可能会长几个像素或很多甚至是一些重要因素。看着你的代码,我怀疑后者..

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

  • 抗锯齿:除非您完全关闭抗锯齿(带来所有难看的后果),否则绘制的像素数也将是所有这些的2-3倍左右还必须计算和绘制中心线上方和下方的抗锯齿像素。不要忘记计算他们的颜色!

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

答案 1 :(得分:1)

如果你真的有很多行或你的线可能很长(屏幕大小的几倍),或者如果你有很多近乎0像素的线,你必须编写代码来减少无用的绘图线条。

嗯,这里有一些想法:

  • 如果你在同一个x上写了很多行,那么你可以在那个x的min和max y之间用一行代替。
  • 如果你的线超出了屏幕边界,你应该剪辑它们。
  • 如果一条线完全位于可见区域之外,则应跳过它。
  • 如果一行的长度为0,则不应该写入。
  • 如果一条线具有单个像素长度,则应仅写入该像素。

显然,好处很大程度上取决于你画了多少行...而另一种选择可能也不会给出完全相同的结果......

实际上,你在屏幕上绘制一个图表,然后如果你只显示有用的信息,它应该在现代硬件上非常快。

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

或者,它们是一些图表组件,它们针对显示大数据进行了优化......好的一个通常很昂贵,但它仍然值得。通常可以进行试验,这样您就可以了解可以提高性能的程度,然后决定做什么。