优化 GDI 的性能功能

发布于 2024-12-09 16:06:22 字数 1241 浏览 0 评论 0原文

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

有没有可能优化它?

  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

在此处输入图像描述

When profiling my GDI+ project I discovered that the following IsLineVisible function is one of the "hottest" while the drawing and moving objects 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

enter image description here

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

一城柳絮吹成雪 2024-12-16 16:06:22

更新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;
    }

更新包括厚度/宽度。

这是完全未经测试的代码,但它应该为您提供超快速解决方案的基本思想,而无需昂贵的框架调用:

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

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);
}
却一份温柔 2024-12-16 16:06:22

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

这将使该方法减少递归并减少对 Widen 的调用,而不会损失太多效果(我希望是最后一个)。

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).

孤君无依 2024-12-16 16:06:22

与其创建一条非常昂贵的 GDI 构造路径,不如循环遍历您的点,将该点与前一个点连接起来,并检查该线是否与您的矩形相交,怎么样?

它的计算成本应该较低,并且能够在与矩形相交的第一段上停止循环。

另一篇文章应该有助于交叉测试。
如何查找直线和矩形?

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?

安穩 2024-12-16 16:06:22

无需创建Region;可以使用 GraphicsPath.IsVisible 代替。我将扩大 GraphicsPath 并将其缓存以供重用,每个需要命中测试的对象。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文