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:
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.
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);
}
}
}
精彩评论