开发者

Optimize the performance of a GDI+ function

开发者 https://www.devze.com 2023-04-12 01:39 出处:网络
When profiling my GDI+ project I discovered that the following IsLineVisible function is one of the \"hottest\" while the drawing and moving objec开发者_Go百科ts on my custom panel.

When profiling my GDI+ project I discovered that the following IsLineVisible function is one of the "hottest" while the drawing and moving objec开发者_Go百科ts on my custom panel.

Is there a possibility to optimize it?

  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

Optimize the performance of a GDI+ function


UPDATE 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;
    }

UPDATED to include thickness/width.

This is completely untested code, but it should give you the basic idea for a hyper-fast solution with no expensive framwork calls:

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);
}


The only thing I can see is perhaps using a wider/thicker Pen.

This will let the method recurse less and cut down the calls to Widen without losing too much of the effect (I hope on the last one).


Instead of creating a path, which is a very expensive GDI construct, how about looping through your points, connecting that point with the previous point, and checking to see if that line intersects with your rectangle?

It should be less computationally expensive, with the bonus of being able to stop the loop on the first segment to intersect the rectangle.

This other post should help with the intersection test. How to find the intersection point between a line and a rectangle?


There is no need to create a Region; GraphicsPath.IsVisible can be used instead. I would widen the GraphicsPath and cache it to be reused, per object that needs hit testing.

0

精彩评论

暂无评论...
验证码 换一张
取 消