画之字形线比画直线慢得多
Drawing zig-zag lines is much slower than drawing straight lines
在使用自己编写的图形控件时,我注意到在显示嘈杂数据时绘制图形比显示干净数据时要慢得多。
我进一步研究并将问题缩小到最小差异:绘制具有不同 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
所以好像是越画越差,画的线条变化越大,这也是我一开始说的控制画遇到的真实世界的效果。
因此我的问题如下:
- 为什么会产生如此残酷的差异,要画哪些线?
- 如何提高嘈杂数据的绘图速度?
想到三个原因:
线长 :根据实际数字,斜线可能会长几个像素或很多,甚至会长一些重要因素。查看您的代码,我怀疑是后者..
算法:绘制斜线确实需要一些算法来找到下一个像素。即使是快速绘图例程也需要进行一些计算,而不是垂直或水平线,后者 运行 直接穿过像素阵列。
抗锯齿 :除非你完全关闭抗锯齿(带来所有丑陋的后果),否则要绘制的像素数也将在 2 左右-3 倍,因为还必须计算和绘制中心线上方和下方的所有抗锯齿像素。不要忘记计算它们的颜色!
后半部分的补救措施显然是关闭抗锯齿,但其他问题就是这样。所以最好不要担心并为快速的直线感到高兴:-)
如果你真的有很多线或者你的线可能很长(屏幕大小的几倍),或者如果你有很多几乎为 0 像素的线,你必须写代码来减少无用的画线。
嗯,这里有一些想法:
- 如果您在同一个 x 处写了很多行,那么您可以在该 x 处用最小和最大 y 之间的一行替换它们。
- 如果您的线条超出屏幕边界,您应该剪掉它们。
- 如果一行完全在可见区域之外,您应该跳过它。
- 如果一行的长度为 0,则不应写入。
- 如果一条线只有一个像素长度,您应该只写那个像素。
显然,好处在很大程度上取决于你画了多少条线......而且替代方案可能不会给出完全相同的结果......
实际上,如果您在屏幕上绘制图表,那么如果您只显示有用的信息,在现代硬件上应该会很快。
好吧,如果您使用样式或颜色,优化数据显示可能就不那么简单了。
或者,它们是一些为显示大数据而优化的图表组件...好的组件通常很贵,但可能仍然值得。通常可以进行试用,这样您就可以很好地了解可以将性能提高多少,然后再决定要做什么。
在使用自己编写的图形控件时,我注意到在显示嘈杂数据时绘制图形比显示干净数据时要慢得多。
我进一步研究并将问题缩小到最小差异:绘制具有不同 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
所以好像是越画越差,画的线条变化越大,这也是我一开始说的控制画遇到的真实世界的效果。
因此我的问题如下:
- 为什么会产生如此残酷的差异,要画哪些线?
- 如何提高嘈杂数据的绘图速度?
想到三个原因:
线长 :根据实际数字,斜线可能会长几个像素或很多,甚至会长一些重要因素。查看您的代码,我怀疑是后者..
算法:绘制斜线确实需要一些算法来找到下一个像素。即使是快速绘图例程也需要进行一些计算,而不是垂直或水平线,后者 运行 直接穿过像素阵列。
抗锯齿 :除非你完全关闭抗锯齿(带来所有丑陋的后果),否则要绘制的像素数也将在 2 左右-3 倍,因为还必须计算和绘制中心线上方和下方的所有抗锯齿像素。不要忘记计算它们的颜色!
后半部分的补救措施显然是关闭抗锯齿,但其他问题就是这样。所以最好不要担心并为快速的直线感到高兴:-)
如果你真的有很多线或者你的线可能很长(屏幕大小的几倍),或者如果你有很多几乎为 0 像素的线,你必须写代码来减少无用的画线。
嗯,这里有一些想法:
- 如果您在同一个 x 处写了很多行,那么您可以在该 x 处用最小和最大 y 之间的一行替换它们。
- 如果您的线条超出屏幕边界,您应该剪掉它们。
- 如果一行完全在可见区域之外,您应该跳过它。
- 如果一行的长度为 0,则不应写入。
- 如果一条线只有一个像素长度,您应该只写那个像素。
显然,好处在很大程度上取决于你画了多少条线......而且替代方案可能不会给出完全相同的结果......
实际上,如果您在屏幕上绘制图表,那么如果您只显示有用的信息,在现代硬件上应该会很快。
好吧,如果您使用样式或颜色,优化数据显示可能就不那么简单了。
或者,它们是一些为显示大数据而优化的图表组件...好的组件通常很贵,但可能仍然值得。通常可以进行试用,这样您就可以很好地了解可以将性能提高多少,然后再决定要做什么。