Оптимизировать производительность функции GDI + - PullRequest
2 голосов
/ 11 октября 2011

При профилировании моего проекта 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

enter image description here

Ответы [ 4 ]

5 голосов
/ 11 октября 2011

ОБНОВЛЕНИЕ 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);
}
0 голосов
/ 14 октября 2011

Нет необходимости создавать регион;Вместо этого можно использовать GraphicsPath.IsVisible.Я бы расширил GraphicsPath и кэшировал его для повторного использования для каждого объекта, который нуждается в проверке попаданий.

0 голосов
/ 11 октября 2011

Вместо создания пути, который является очень дорогой конструкцией GDI, как насчет циклического прохождения ваших точек, соединения этой точки с предыдущей точкой и проверки, пересекает ли эта линия ваш прямоугольник?

Он должен быть менее затратным в вычислительном отношении, а также иметь возможность останавливать цикл в первом сегменте для пересечения прямоугольника.

Этот другой пост должен помочь с тестом на пересечение. Как найти точку пересечения линии и прямоугольника?

0 голосов
/ 11 октября 2011

Единственное, что я вижу, это, возможно, использование более широкого / более толстого Pen.

. Это позволит методу меньше рекурсировать и сократить вызовы до Widen, не теряя слишком много эффекта (Надеюсь на последний).

...