2015-01-16 54 views
2

這一個很瘋狂。我只需在OnPaint處理程序中繪製幾千行。當pen.Width <= 1或屏幕上沒有多少行時,沒有問題。使用WinForms繪製寬線時發生OutOfMemoryException

好吧,我畫一個縮放的地圖。線寬與地圖成比例。當我放大某些地圖時,我得到OutOfMemoryException。爲什麼?!

當我設置pen.Width爲1 - 沒有問題。當我將它設置爲對應軌道寬度時 - 某些地圖繪製正常,某些地圖會在某些縮放級別拋出異常。

發生了什麼事?這與實際的內存使用沒有任何關係。我已經仔細檢查了這一點。

順便說一下,pen.Width我設置的時候發生了2左右。

該代碼看起來像foreach (...) g.DrawLine(...) - 它在繪製幾百行後崩潰。

如果我不能找到解決方案,我將不得不放棄線寬縮放,這將大大降低演示質量。或者我可以做一個醜陋的黑客試圖趕上這個例外(如果它可以被捕獲)...

注:我沒有使用任何位圖。我不操作龐大的陣列。繪圖時我不打開任何文件。有一組向量(大約10k個元素),我只是將它們全部繪製爲單獨的行,對各種地圖對象使用一些不同的筆。當我不碰pen.Width - 沒有發生異常。當我設置pen.Width時 - 一些地圖可以正確顯示所有縮放級別,但有些會拋出異常。在進入繪圖循環之前,會在OnPaint事件中創建5筆,並在退出循環後正確放置。繪製每條線之前,其寬度已設置。

我試圖限制線座標只有在實際可見的視口。這是多餘的,因爲Graphics對象自己處理它。當然它沒有幫助。我嘗試了一些較小的窗口大小 - 沒有幫助。我試圖打開和關閉雙緩衝。沒有快樂。我沒有想法。

編輯:

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

看到醜陋的黑客在這裏?它完美地工作,我甚至不會看到哪條曲線被線代替。顯然g.DrawBezier引發異常。我不喜歡醜陋的黑客...

+1

關於'OutOfMemoryException',你會分享你的完整堆棧跟蹤嗎? –

+1

請參閱[GraphicsPath和OutOfMemoryException](http://stackoverflow.com/q/6927774/719186) – LarsTech

+0

這可能是!謝謝你,我明天會檢查一下,但這會加起來。 – Harry

回答

2

這裏的解決方案,從感謝@LarsTech暗示:

private void DrawMap(PaintEventArgs e) { 
    var pens = new[] { // TODO: draw layers instead 
     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; // (TODO: limiting vectors to visible ones) 
    var tr = GetTransformation(); // gets scale and translation for points 
    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); 
      var b1c = Math.Abs(p1.X - p2.X) >= 0.1f || Math.Abs(p1.Y - p2.Y) > 0.1f; 
      var b2c = Math.Abs(p3.X - p4.X) >= 0.1f || Math.Abs(p3.Y - p4.Y) > 0.1f; 
      if (b1c && b2c) g.DrawBezier(pen, p1, p2, p3, p4); else g.DrawLine(pen, p1, p4); 
     } 
    } 
    foreach (var p in pens) p.Dispose(); 
} 

在他linked答案,我們讀到:

這是一個錯誤與筆和擴大的方法。確保路徑的起點和路徑的終點不相同。

是的,.NET中的一個bug,向微軟報告,顯然還沒有修復。在這裏,它顯示了貝塞爾曲線,它看起來太像直線;)

我猜零長度線可能會拋出類似的異常。

請注意,我檢查點座標之間的距離大於0.1f,而不是0!這一點很重要。如果兩點之間的距離足夠接近,那麼就會拋出異常,而不僅僅當它們相等時。我可以計算點之間的距離,但出於性能原因,最好不要。

對於每條曲線和每條線都進行這種檢查對於性能來說並不好 - 但它似乎比捕獲錯誤的異常好得多。該檢查可能可能會優化一點,或移動到「規模變化」的處理程序。

順便說一句:GetTransformation()在我的代碼中的方法只是爲所有點帶來比例尺,X和Y偏移量。如果您想知道爲什麼我不使用內置轉換並手動執行 - 這是因爲內置轉換不適用於雙緩衝。在.NET中的另一個錯誤或只是功能?如果沒有雙緩衝繪圖是非常緩慢的,所以它必須在這裏使用。