GraphicsPath and OutOfMemoryException

偶尔善良 提交于 2019-12-12 13:07:16

问题


I have the following

private bool IsPathVisible(Rectangle detectorRectangle, GraphicsPath path, Pen pen)
{
    path.Widen(pen);
    return IsPathVisible(detectorRectangle, path);
}

When path points are the same point, I receive a OutOfMemoryException (using Widen function).

How can I manage it?


回答1:


That's a bug with the pen and the widen method. Make sure your startpoint of the path and the endpoint of the path are not the same.

This is a demonstration:

private void panel1_Paint(object sender, PaintEventArgs e)
{
  //This works:
  using (GraphicsPath path = new GraphicsPath())
  {
    path.AddLine(new Point(16, 16), new Point(20, 20));
    path.Widen(Pens.Black);
    e.Graphics.DrawPath(Pens.Black, path);
  }

  //This does not:
  using (GraphicsPath path = new GraphicsPath())
  {
    path.AddLine(new Point(20, 20), new Point(20, 20));
    path.Widen(Pens.Black);
    e.Graphics.DrawPath(Pens.Black, path);
  }
}

Here is where it was reported to Microsoft: GraphicsPath.Widen throw OutOfMemoryException if the path has a single point




回答2:


I've also been suffering from this exception. Recommendations are as following:

  1. Save the points before widening to see exact points that cause OutOfMemoryException:

    private bool IsPathVisible(Rectangle detectorRectangle, GraphicsPath path, Pen pen)
    {
        var points = path.PathPoints.Clone() as PointF[];
        path.Widen(pen);
        return IsPathVisible(detectorRectangle, path);
    }
    

What you might see is that there are probably consequent points that have the same coordinates. They are actually causing the problem.

  1. Also, GraphicsPath can consist of multiple subpaths. To make a reliable hit testing, I would recommend the following:

    public Region[] CreateRegionFromGraphicsPath(GraphicsPath path, Pen wideningPen)
    {
        var regions = new List<Region>();
    
        var itPath = new GraphicsPathIterator(path);
        itPath.Rewind();
        var curSubPath = new GraphicsPath();
    
        for (int i = 0; i < itPath.SubpathCount; i++)
        {
            bool isClosed;
            itPath.NextSubpath(curSubPath, out isClosed);
    
            if (!isClosed && CanWiden(curSubPath)) curSubPath.Widen(wideningPen); // widen not closed paths
    
            int regionIndex = i / 100; // max region scan rectangles count
    
            if (regions.Count < regionIndex + 1)
            {
                regions.Add(new Region(curSubPath));
            }
            else
            {
                regions[regionIndex].Union(curSubPath);
            }
        }
    
        curSubPath.Dispose();
        itPath.Dispose();
    
        return regions.ToArray();
    }
    
    /// <summary>
    /// Determines whether widening this graphics path will not lead to an exception
    /// </summary>
    public static bool CanWiden(GraphicsPath gp)
    {
        const float regionPointsTolerance = 1e-8f;
        var pts = gp.PathPoints;
        if (pts.Length < 2) return false;
        for (int i = 1; i < pts.Length; i++)
        {
            if (Math.Abs(pts[i-1].X - pts[i].X) < regionPointsTolerance && Math.Abs(pts[i-1].Y - pts[i].Y) < regionPointsTolerance) return false;
        }
        return true;
    }
    

Then you simply call IsVisible for regions to find if any of them is hit




回答3:


if the path IsPoint, don't do Widen.

<System.Runtime.CompilerServices.Extension()> _
Public Function IsPoint(ByVal path As System.Drawing.Drawing2D.GraphicsPath) As Boolean
    If path Is Nothing Then Throw New ArgumentNullException("path")

    If path.PathPoints.Count < 2 Then Return True

    If path.PathPoints(0) <> path.PathPoints(path.PathPoints.Count - 1) Then Return False

    For i = 1 To path.PathPoints.Count - 1
     If path.PathPoints(i - 1) <> path.PathPoints(i) Then Return False
    Next i

    ' if all the points are the same
    Return True
End Function 



回答4:


The following code causes OutOfMemory in DrawPath in .Net 4.0 (and possibly higher). I was managed to bypass it using LineCap.Flat instead of LineCap.NoAnchor:

public void TestDrawPath()
{
    PointF[] points = new PointF[13]
    {
        new PointF(0.491141558f, 1.53909028f),
        new PointF(0.491141558f, 1.55148673f),
        new PointF(0.4808829f, 1.56153619f),
        new PointF(0.468228281f, 1.56153619f),
        new PointF(0.4555736f, 1.56153619f),
        new PointF(0.445314974f, 1.55148673f),
        new PointF(0.445314974f, 1.53909028f),
        new PointF(0.445314974f, 1.52669382f),
        new PointF(0.4555736f, 1.51664436f),
        new PointF(0.468228281f, 1.51664436f),
        new PointF(0.4808829f, 1.51664436f),
        new PointF(0.491141558f, 1.52669382f),
        new PointF(0.491141558f, 1.53909028f)
    };
    byte[] types = new byte[13] { 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 131 };

    using (Bitmap bitmap = new Bitmap(2, 2))
    using (Graphics g = Graphics.FromImage(bitmap))
    {
        using (Pen pen = new Pen(Color.Black))
        using (GraphicsPath path = new GraphicsPath(points, types))
        {
            pen.StartCap = LineCap.NoAnchor;
            pen.EndCap = LineCap.NoAnchor;
            g.DrawPath(pen, path);
        }
    }
}


来源:https://stackoverflow.com/questions/6927774/graphicspath-and-outofmemoryexception

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!