开发者

GraphicsPath and OutOfMemoryException

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 (us开发者_Python百科ing Widen function).

How can I manage it?


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


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


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 


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

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜