优化GDI +功能的性能

问题描述:

在分析我的GDI +项目时,我发现以下IsLineVisible函数是在我的自定义面板上绘制和移动对象时“最热”的函数之一。优化GDI +功能的性能

有没有可能优化它?

Private Function IsLineVisible(ByVal detectorRectangle As Rectangle, 
           ByVal pen As Pen, 
           ByVal ParamArray points() As Point) As Boolean 
    Using path As New GraphicsPath() 
     path.AddLines(points) 
     Return IsPathVisible(detectorRectangle, path, pen) 
    End Using 
    End Function 

    ' Helper functions ''''''''''''''''''''''''''''''''''''' 
    Private Function IsPathVisible(ByVal detectorRectangle As Rectangle, 
           ByVal path As GraphicsPath, 
           ByVal pen As Pen) As Boolean 
    If Not path.IsPoint Then 
     path.Widen(pen) 
    End If 
    Return IsPathVisible(detectorRectangle, path) 
    End Function 


    Private Function IsPathVisible(ByVal detectorRectangle As Rectangle, 
           ByVal path As GraphicsPath) As Boolean 
    Using r As New Region(path) 
     If r.IsVisible(detectorRectangle) Then 
     Return True 
     Else 
     Return False 
     End If 
    End Using 
    End Function 

enter image description here

更新2:

public bool AreLinesVisible(Point[] p, int width, Rectangle rect) 
    { 
     for (var i = 1; i < p.Length; i++) 
      if (IsLineVisible(p[i - 1], p[i], width, rect)) 
       return true; 
     return false; 
    } 

修订包括厚度/宽度。

这是完全未经测试的代码,但它应该给你基本思路对于没有昂贵的framwork一个超快速的解决方案要求:

public bool IsLineVisible(Point p1, Point p2, int width, Rectangle rect) 
{ 
    var a = Math.Atan2(p1.Y - p2.Y, p1.X - p2.X) + Math.PI/2; 
    var whalf = (width + 1)*0.5; 
    var dx = (int) Math.Round(whalf*Math.Sin(a)); 
    var dy = (int) Math.Round(whalf*Math.Cos(a)); 
    return IsLineVisible(new Point(p1.X - dx, p1.Y - dy), new Point(p2.X - dx, p2.Y - dy), rect) 
     || IsLineVisible(new Point(p1.X + dx, p1.Y + dy), new Point(p2.X + dx, p2.Y + dy), rect); 
} 

public bool IsLineVisible(Point p1, Point p2, Rectangle rect) 
{ 
    if (p1.X > p2.X) // make sure p1 is the leftmost point 
     return IsLineVisible(p2, p1, rect); 

    if (rect.Contains(p1) || rect.Contains(p2)) 
     return true; // one or both end-points within the rect -> line is visible 

    //if both points are simultaneously left or right or above or below -> line is NOT visible 
    if (p1.X < rect.X && p2.X < rect.X) 
     return false; 
    if (p1.X >= rect.Right && p2.X >= rect.Right) 
     return false; 
    if (p1.Y < rect.Y && p2.Y < rect.Y) 
     return false; 
    if (p1.Y >= rect.Bottom && p2.Y >= rect.Bottom) 
     return false; 

    // now recursivley break down the line in two part and see what happens 
    // (this is an approximation...) 
    var pMiddle = new Point((p1.X + p2.X)/2, (p1.Y + p2.Y)/2); 
    return IsLineVisible(p1, new Point(pMiddle.X - 1, pMiddle.Y), rect) 
      || IsLineVisible(new Point(pMiddle.X + 1, pMiddle.Y), p2, rect); 
} 
+0

谢谢。但是你“忘记”了笔的宽度**(厚度)。 – serhio

+0

是的,但我说的基本思想 - 你只需要计算两条轮廓线,组成你的粗线并通过它们。如果其中一个是可见的 - 那么你的粗线是可见的! –

+0

这条线也可以有一个样式:虚线,虚线... – serhio

我能看到的唯一的事情是可能使用更宽/更厚Pen

这样可以减少方法,减少对Widen的调用,而不会失去太多的效果(我希望在最后一个)。

+0

我使用其他笔,我通过作为参数...这是所有的重点。我有一些宽线,其他粗线,所以我应该检测不同类型的线... – serhio

而不是创建一个路径,这是一个非常昂贵的GDI构造,如何循环你的点,将该点与前一点连接,并检查该线是否与矩形相交?

它应该在计算上花费较少,并且能够停止第一个段上的循环与矩形相交。

这篇文章应该有助于交集测试。 How to find the intersection point between a line and a rectangle?

+0

这个想法是笔,可以是不同的。不只是一行像素的故事。 – serhio

有没有必要建立一个区域; GraphicsPath.IsVisible可以用来代替。我会扩大GraphicsPath并将其缓存以供每个需要命中测试的对象重用。