This one is crazy. I just draw a couple of thousands of lines in OnPaint
handler. There is no problem, when pen.Width <= 1
, or when there aren't many lines on the screen.
OK, I draw a scaled map. Line width scales with the map. When I zoom SOME maps, I get OutOfMemoryException
. WHY?!
When I set pen.Width
to 1 - no problem. When I set it to correspond tracks widths - some maps draw OK, some throw the exception AT CERTAIN ZOOM LEVELS.
What's going on? It's has NOTHING to do with actual memory usage. I've double checked this.
BTW, the pen.Width
I set is around 2 when it happens.
The code looks like foreach (...) g.DrawLine(...)
- and it crashes after drawing a couple of hundreds of lines.
If I won't find solution to this, I'll have to drop line width scaling which would greatly degrade the quality of presentation. Or I can do an ugly hack trying to catch this exception (if it can be caught)...
NOTE: I don't use any bitmaps. I don't operate on huge arrays. I don't open any files during drawing. There is an array of vectors (about 10k elements), I just draw all of them as separate lines, using some different pens for various map objects. When I don't touch pen.Width
- no exception occurs. When I set pen.Width - some maps are displayed correctly with all zoom levels, but some throw the exception. The 5 pens are created in OnPaint
event before entering the drawing loop and are properly disposed after exiting the loop. Before drawing each line its width is set.
I tried to limit line coordinates to only ones actually visible in viewport. It's redundant, since Graphics
object takes care of it by itself. Of course it didn't help. I tried it on some smaller window sizes - didn't help. I tried to switch double buffering on and off. No joy. I'm out of ideas.
EDIT:
private void DrawMap(PaintEventArgs e) {
var pens = new[] {
new Pen(TrackColor),
new Pen(SwitchColor),
new Pen(RoadColor),
new Pen(RiverColor),
new Pen(CrossColor)
};
var b = Splines.Bounds;
var g = e.Graphics;
var f = true; // OutFull;
var tr = GetTransformation();
float ts = tr[0], tx = tr[1], ty = tr[2];
TrackSpline[] visible = !f ? Splines.GetSubset(ts, _Viewport) : null;
var ct = f ? Splines.Count : visible.Length;
for (int i = 0; i < ct; i++) {
TrackSpline s = f ? Splines[i] : visible[i];
var pen = pens[s.T];
pen.Width = ts * s.W;
if (ts < 0.01 || s.L) {
var p1 = new PointF(s.A.X * ts + tx, s.A.Y * ts + ty);
var p2 = new PointF(s.D.X * ts + tx, s.D.Y * ts + ty);
g.DrawLine(pen, p1, p2);
} else {
var p1 = new PointF(s.A.X * ts + tx, s.A.Y * ts + ty);
var p2 = new PointF(s.B.X * ts + tx, s.B.Y * ts + ty);
var p3 = new PointF(s.C.X * ts + tx, s.C.Y * ts + ty);
var p4 = new PointF(s.D.X * ts + tx, s.D.Y * ts + ty);
try {
g.DrawBezier(pen, p1, p2, p3, p4);
} catch (OutOfMemoryException) {
g.DrawLine(pen, p1, p4);
}
}
}
foreach (var p in pens) p.Dispose();
}
See the ugly hack here? It works flawlessly and I don't even see which curves are replaced with lines. Obviously g.DrawBezier
throws the exception. I don't like ugly hacks...