2011-10-14 68 views
11

我有一個小型的繪畫程序,我正在研究。我在位圖上使用SetPixel來繪製線條。當畫筆大小變大時,像25個像素一樣,會有明顯的性能下降。我想知道是否有更快的方式繪製到位圖。這裏有一些項目的背景:SetPixel太慢。有沒有更快的方法來繪製位圖?

  • 我使用的位圖,以便我可以利用圖層,就像在Photoshop或GIMP。
  • 線條正在手動繪製,因爲這將最終使用圖形輸入板壓力來改變線條在其長度上的大小。
  • 這些線條最終應沿邊緣反斜線/平滑。

我會包含我的繪圖代碼,以防萬一它是慢的而不是Set-Pixel位。

這在繪畫發生在窗口:

private void canvas_MouseMove(object sender, MouseEventArgs e) 
    { 
     m_lastPosition = m_currentPosition; 
     m_currentPosition = e.Location; 

     if(m_penDown && m_pointInWindow) 
      m_currentTool.MouseMove(m_lastPosition, m_currentPosition, m_layer); 
     canvas.Invalidate(); 
    } 

的MouseMove的執行情況:

public override void MouseMove(Point lastPos, Point currentPos, Layer currentLayer) 
    { 
     DrawLine(lastPos, currentPos, currentLayer); 
    } 

的DrawLine的執行情況:

// The primary drawing code for most tools. A line is drawn from the last position to the current position 
    public override void DrawLine(Point lastPos, Point currentPos, Layer currentLayer) 
    { 
     // Creat a line vector 
     Vector2D vector = new Vector2D(currentPos.X - lastPos.X, currentPos.Y - lastPos.Y); 

     // Create the point to draw at 
     PointF drawPoint = new Point(lastPos.X, lastPos.Y); 

     // Get the amount to step each time 
     PointF step = vector.GetNormalisedVector(); 

     // Find the length of the line 
     double length = vector.GetMagnitude(); 

     // For each step along the line... 
     for (int i = 0; i < length; i++) 
     { 
      // Draw a pixel 
      PaintPoint(currentLayer, new Point((int)drawPoint.X, (int)drawPoint.Y)); 
      drawPoint.X += step.X; 
      drawPoint.Y += step.Y; 
     } 
    } 

實施PaintPoint的:

public override void PaintPoint(Layer layer, Point position) 
    { 
     // Rasterise the pencil tool 

     // Assume it is square 

     // Check the pixel to be set is witin the bounds of the layer 

      // Set the tool size rect to the locate on of the point to be painted 
     m_toolArea.Location = position; 

      // Get the area to be painted 
     Rectangle areaToPaint = new Rectangle(); 
     areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea); 

      // Check this is not a null area 
     if (!areaToPaint.IsEmpty) 
     { 
      // Go through the draw area and set the pixels as they should be 
      for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++) 
      { 
       for (int x = areaToPaint.Left; x < areaToPaint.Right; x++) 
       { 
        layer.GetBitmap().SetPixel(x, y, m_colour); 
       } 
      } 
     } 
    } 

非常感謝任何幫助,您可以提供。

回答

11

可以鎖定位圖數據,並使用指針手動設定值進行排序性能下降的問題。它快得多。儘管你必須使用不安全的代碼。

public override void PaintPoint(Layer layer, Point position) 
    { 
     // Rasterise the pencil tool 

     // Assume it is square 

     // Check the pixel to be set is witin the bounds of the layer 

     // Set the tool size rect to the locate on of the point to be painted 
     m_toolArea.Location = position; 

     // Get the area to be painted 
     Rectangle areaToPaint = new Rectangle(); 
     areaToPaint = Rectangle.Intersect(layer.GetRectangle(), m_toolArea); 

     Bitmap bmp; 
     BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); 
     int stride = data.Stride; 
     unsafe 
     { 
      byte* ptr = (byte*)data.Scan0; 
      // Check this is not a null area 
      if (!areaToPaint.IsEmpty) 
      { 
       // Go through the draw area and set the pixels as they should be 
       for (int y = areaToPaint.Top; y < areaToPaint.Bottom; y++) 
       { 
        for (int x = areaToPaint.Left; x < areaToPaint.Right; x++) 
        { 
         // layer.GetBitmap().SetPixel(x, y, m_colour); 
         ptr[(x * 3) + y * stride] = m_colour.B; 
         ptr[(x * 3) + y * stride + 1] = m_colour.G; 
         ptr[(x * 3) + y * stride + 2] = m_colour.R; 
        } 
       } 
      } 
     } 
     bmp.UnlockBits(data); 
    } 
+0

謝謝你。我想我知道發生了什麼; ptr是包含紅色,綠色和藍色像素值的位圖數據的集合,並且正在手動設置它們。 今天我會告訴大家一切,看看它是如何實現的。非常感謝,我覺得我的理解進一步提高了一個檔次:P 只是一件事,我認爲C#中的位圖也有一個alpha通道。我認爲這將與RGB一起在像素顏色信息中處理。 – Pyro

+1

您需要指定http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx以包含alpha組件。只需將'PixelFormat.Format24bppRgb'改爲'PixelFormat.Format32bppArgb'。我相信它應該在R之後,以+3抵消。如果每個像素使用32位,則需要將'x * 3'更改爲'x * 4',因爲現在每個像素都是4個字節。 – Jack

+0

我剛剛試過這個。現在在所有的筆刷尺寸上都非常可愛且快速!但是現在有一些奇怪的行爲:實際繪圖發生在鼠標所在位置的偏移處。 我不知道它什麼做這行的位置: 的BitmapData數據= bmp.LockBits(新的Rectangle(areaToPaint.Right,areaToPaint.Bottom),ImageLockMode.ReadWrite,PixelFormat.Format24bppRgb); 需要爲矩形指定一個位置。我使用提供的'位置'值。是對的嗎? – Pyro

0

只是一個想法:用畫筆像素填充離屏位圖。刷子,大小或顏色改變時,您只需重新生成此位圖。然後,只需將此位圖繪製到鼠標所在位置的現有位圖上即可。 如果您可以使用某種顏色調製位圖,則可以將像素設置爲灰度並使用當前畫筆顏色對其進行調製。

+0

這聽起來像一個很酷的想法。謝謝。我想如果我使用透明度,那麼在繪製現有線條時它會起作用。 – Pyro

5

SetPixel做到這一點: 是鎖定整個圖像,設置像素,並解鎖它

嘗試做的是:你獲得與lockbits整個存儲圖像的鎖,工藝更新後解除鎖定。

lockbits

+0

因此,我鎖定位圖,做我的繪圖代碼,然後解鎖它? – Pyro

+0

我舉個例子,一會兒...... – fixagon

+18

3年後,仍然沒有例子 –

3

我通常使用的陣列來表示原始像素數據。然後使用不安全的代碼在該數組和位圖之間進行復制。

製作Color的數組是一個壞主意,因爲Color結構比較大(12字節+)。所以你可以定義你自己的4字節結構(這是我選擇的結構),或者簡單地使用intbyte的數組。

您還應該重用您的陣列,因爲GC上的LOH往往很昂貴。

https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/

另一種是直接使用指針進位所有寫代碼:

我的代碼可以在這裏找到。這還有點快,但可以使代碼更醜,更容易出錯。

+1

您是否使用一個數組大小的目標位圖或只有大小該區域將被修改? – Pyro

+0

在我的應用程序中,我使用了與位圖大小相同的數組,但我需要處理整個位圖。但即使我只在部分位圖上工作,我仍然會使用整個數組,但可能只填寫我需要的部分。這樣,我不需要在處理圖像的不同大小的部分時分配新的數組。 – CodesInChaos

0

你在嵌套for循環中調用GetBitmap。看起來沒有必要,你應該將GetBitmap放在for循環之外,因爲引用不會改變。

也期待在@fantasticfix答案,Lockbits幾乎總是與獲取/設置像素

相關問題