使用 GDI 使用不同颜色绘制线条的快速方法+?
我有一个动态的点列表,可以随时添加新点。我想用不同的颜色画线来连接它们。颜色基于这些点的索引。代码如下:
private List<Point> _points;
private static Pen pen1 = new Pen(Color.Red, 10);
private static Pen pen2 = new Pen(Color.Yellow, 10);
private static Pen pen3 = new Pen(Color.Blue, 10);
private static Pen pen4 = new Pen(Color.Green, 10);
private void Init()
{
// use fixed 80 for simpicity
_points = new List<Point>(80);
for (int i = 0; i < 80; i++)
{
_points.Add(new Point(30 + i * 10, 30));
}
}
private void DrawLinesNormal(PaintEventArgs e)
{
for (int i = 0; i < _points.Count-1; i++)
{
if (i < 20)
e.Graphics.DrawLine(pen1, _points[i], _points[i + 1]);
else if (i < 40)
e.Graphics.DrawLine(pen2, _points[i], _points[i + 1]);
else if (i < 60)
e.Graphics.DrawLine(pen3, _points[i], _points[i + 1]);
else
e.Graphics.DrawLine(pen4, _points[i], _points[i + 1]);
}
}
当我有新点高速进入时,我发现这种方法不够快。有什么办法可以让它更快吗?我做了一些研究,有人说使用 GraphicsPath 可能会更快,但是如何呢?
[更新] 我收集了一些可能的优化:
- 使用 GrahpicsPath,原始问题
- 更改图形质量(例如SmoothingMode/PixelOffsetMode...),还调用SetClip来指定唯一需要渲染的区域。
I have an dynamic List of Point, new Point can be added at any time. I want to draw lines to connect them using different color. Color is based on the index of those points. Here is the code:
private List<Point> _points;
private static Pen pen1 = new Pen(Color.Red, 10);
private static Pen pen2 = new Pen(Color.Yellow, 10);
private static Pen pen3 = new Pen(Color.Blue, 10);
private static Pen pen4 = new Pen(Color.Green, 10);
private void Init()
{
// use fixed 80 for simpicity
_points = new List<Point>(80);
for (int i = 0; i < 80; i++)
{
_points.Add(new Point(30 + i * 10, 30));
}
}
private void DrawLinesNormal(PaintEventArgs e)
{
for (int i = 0; i < _points.Count-1; i++)
{
if (i < 20)
e.Graphics.DrawLine(pen1, _points[i], _points[i + 1]);
else if (i < 40)
e.Graphics.DrawLine(pen2, _points[i], _points[i + 1]);
else if (i < 60)
e.Graphics.DrawLine(pen3, _points[i], _points[i + 1]);
else
e.Graphics.DrawLine(pen4, _points[i], _points[i + 1]);
}
}
I find this method is not fast enough when I have new points coming in at a high speed. Is there any way to make it faster? I did some research and someone said using GraphicsPath could be faster, but how?
[UPDATE] I collect some possible optimizations:
- Using GrahpicsPath, Original Question
- Change Graphics quality ( such as SmoothingMode/PixelOffsetMode...), also call SetClip to specify the only necessary region to render.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
在不损失质量或更改为更快的渲染器(GDI、OpenGL、DirectX)的情况下,您将无法从该代码中获得更高的速度。但 GDI 通常会快很多(可能是 2 倍),而 DirectX/OpenGL 可能会快得多(可能是 10 倍),具体取决于您绘制的内容。
使用 Path 的想法是将许多(在您的示例中为 20)行批处理到单个方法调用中,而不是调用 DrawLine 20 次。仅当您可以将传入数据排列为绘图例程的正确点列表格式时,这才会对您有利。否则,您将必须将点复制到正确的数据结构中,这将浪费您通过批处理到路径中获得的大量时间。对于 DrawPath,您可能必须从点数组创建 GraphicsPath,这可能会导致无法节省时间。但是,如果您必须多次绘制相同的路径,则可以将其缓存,然后您可能会看到净收益。
如果新的点被添加到列表中,但旧的点没有被删除(即您总是只是将新的行添加到显示中),那么您将能够使用屏幕外位图来存储到目前为止渲染的行。这样,每次添加一个点时,您都会绘制一条线,而不是每次都绘制全部 80 条线。
这完全取决于您想要做什么。
You won't be able to squeeze much more speed out of that code without losing quality or changing to a faster renderer (GDI, OpenGL, DirectX). But GDI will often be quite a bit faster (maybe 2x), and DirectX/OpenGL can be much faster (maybe 10x), depending on what you're drawing.
The idea of using a Path is that you batch many (in your example, 20) lines into a single method call, rather than calling DrawLine 20 times. This will only benefit you if you can arrange the incoming data into the correct list-of-points format for the drawing routine. Otherwise, you will have to copy the points into the correct data structure and this will waste a lot of the time that you are gaining by batching into a path. In the case of DrawPath, you may have to create a GraphicsPath from an array of points, which may result in no time saved. But if you have to draw the same path more than once, you can cache it, and you may then see a net benefit.
If new points are added to the list, but old ones are not removed (i.e. you are always just adding new lines to the display) then you would be able to use an offscreen bitmap to store the lines rendered so far. That way each time a point is added, you draw one line, rather than drawing all 80 lines every time.
It all depends on exactly what you're trying to do.
并没有真正帮助提高性能,但我也会将笔放入列表中并以这种方式编写所有这些行:
Doesn't really help to improve performance, but i would put the pens also into a list and writing all this lines in this way:
这与您使用 System.Drawing 所能达到的速度差不多。使用
Graphics.DrawLines()
可能会带来一些好处,但您需要以不同的方式格式化数据,才能获得使用同一笔同时绘制一堆线条的优势。我严重怀疑GraphicsPath
会更快。提高速度的一种可靠方法是降低输出质量。将
Graphics.InterpolationMode
设置为InterpolationMode.Low
,将Graphics.CompositingQuality
设置为CompositingQuality.HighSpeed
,Graphics. SmoothingMode
为SmoothingMode.HighSpeed
,Graphics.PixelOffsetMode
为PixelOffsetMode.HighSpeed
和Graphics.CompositingMode
为CompositingMode.SourceCopy
。我记得一次速度测试,有人将图形与 GDI 例程中的 P/Invoke 进行比较,并对更快的 P/Invoke 速度感到非常惊讶。你可以检查一下。我会看看是否能找到这种比较...显然这是针对 Compact Framework 的,因此它可能不适用于 PC。另一种方法是使用 Direct2D,如果您有合适的硬件,它可能比 GDI 更快。
This is about as fast as you're going to get with System.Drawing. You might see a bit of gain using
Graphics.DrawLines()
, but you'd need to format your data differently to get the advantage of drawing a bunch of lines at once with the same pen. I seriously doubtGraphicsPath
will be faster.One sure way to improve speed is to reduce the quality of the output. Set
Graphics.InterpolationMode
toInterpolationMode.Low
,Graphics.CompositingQuality
toCompositingQuality.HighSpeed
,Graphics.SmoothingMode
toSmoothingMode.HighSpeed
,Graphics.PixelOffsetMode
toPixelOffsetMode.HighSpeed
andGraphics.CompositingMode
toCompositingMode.SourceCopy
.I remember a speed test once where someone compared Graphics to P/Invoke into GDI routines, and was quite surprised by the much faster P/Invoke speeds. You might check that out. I'll see if I can find that comparison...Apparently this was for the Compact Framework, so it likely doesn't hold for a PC.The other way to go is to use Direct2D, which can be faster yet than GDI, if you have the right hardware.
为时已晚,但可能有人仍然需要解决方案。
我创建了一个小型库 GLGDI+,它具有类似(但不是完整/相等)的 GDI+ 语法,它在 OpenTK 上运行: http://code.google.com/p/glgdiplus/
我不确定稳定性,它在 DrawString 方面存在一些问题(来自 OpenTK 的 TextPrint 存在问题)。但如果您需要提高实用程序的性能(例如我的情况下的关卡编辑器),它可以是解决方案。
Too late, but possibly somebody still need a solution.
I've created small library GLGDI+ with similiar (but not full/equal) GDI+ syntax, which run upon OpenTK: http://code.google.com/p/glgdiplus/
I'm not sure about stability, it has some issues with DrawString (problem with TextPrint from OpenTK). But if you need performance boost for your utility (like level editor in my case) it can be solution.
您可能想研究 Brush 对象,确实您无法从 GDI+ 程序中获得接近实时的性能,但只要几何形状和对象数量保持在合理的范围内,您就可以轻松保持不错的 fps 。至于画线,我不明白为什么不。
但是,如果您达到了您认为最佳的程度,即绘制线条..您应该考虑不同的图形堆栈,如果您喜欢 .NET 但对 OpenGL 和 DirectX 等非托管 API 有问题,请转到配合WPF或Silverlight,它的功能相当强大。
无论如何,您可以尝试设置 System.Drawing.Drawing2D.GraphicsPath,然后使用 System.Drawing.Drawing2D.PathGradientBrush 以这种方式应用颜色。这是一个缓冲的绘制调用,如果您无法从中获得足够的性能。你必须使用 GDI+ 之外的其他东西
You might wanna look into the Brush object, and it's true that you won't get near real-time performance out of a GDI+ program, but you can easily maintain a decent fps as long as the geometry and number of objects stay within reasonable bounds. As for line drawing, I don't see why not.
But if you reach the point where you doing what you think is optimal, and all that is, drawing lines.. you should consider a different graphics stack, and if you like .NET but have issues with unmanaged APIs like OpenGL and DirectX, go with WPF or Silverlight, it's quite powerful.
Anyway, you could try setting up a System.Drawing.Drawing2D.GraphicsPath and then using a System.Drawing.Drawing2D.PathGradientBrush to a apply the colors this way. That's a single buffered draw call and if you can't get enough performance out of that. You'll have to go with something other entirely than GDI+
根本不是 GDI(+),但解决这个问题的一种完全不同的方法可能是使用一块内存,在其中绘制线条,将其转换为 Bitmap 对象以立即绘制您想要的位置需要展示你的台词。
当然,这在很大程度上取决于
我认为不在 .NET Framework 中,但也许在第三方库中? Silverlight 中是否没有类似的位图编写器这? (我自己还不太喜欢 Silverlight...)
至少这可能是一种开箱即用的方法来解决这个问题。希望有帮助。
Not GDI(+) at all, but a completely different way to tackle this could be to work with a block of memory, draw your lines into there, convert it to a
Bitmap
object to instantly paint where you need to show your lines.Of course this hinges in the extreme on fast ways to
Bitmap
to show.Not in the .NET Framework, I think, but perhaps in a third party library? Isn't there a bitmap writer of sorts in Silverlight for stuff like this? (Not into Silverlight myself that much yet...)
At least it might be an out of the box way to approach this. Hope it helps.
我认为你必须在绘制后处理 pen 对象和 e.Graphics 对象。
另一件事是,如果你在 onPaint() 中编写drawLine 代码会更好。
I think you have to dispose pen object and e.Graphics object after drawing.
One more thing it is better if you write your drawLine code inside onPaint().