2011-10-14 32 views
11

Tôi có một chương trình vẽ nhỏ mà tôi đang làm việc. Tôi đang sử dụng SetPixel trên một bitmap để vẽ bản vẽ đó. Khi kích thước bàn chải lớn, như 25 điểm ảnh trên đó là một hiệu suất đáng chú ý thả. Tôi tự hỏi nếu có một cách nhanh hơn để vẽ một bitmap. Dưới đây là một chút nền của dự án:SetPixel quá chậm. Có cách nào nhanh hơn để vẽ lên bitmap không?

  • Tôi đang sử dụng bitmap để tôi có thể sử dụng các lớp, như trong Photoshop hoặc GIMP.
  • Các dòng đang được vẽ theo cách thủ công vì điều này cuối cùng sẽ sử dụng áp lực của máy tính bảng đồ họa để thay đổi kích thước của đường theo chiều dài của nó.
  • Các dòng cuối cùng sẽ được chống xung/làm mịn dọc theo các cạnh.

Tôi sẽ bao gồm mã vẽ của mình trong trường hợp nó chậm và không phải là bit Set-Pixel.

này nằm trong cửa sổ nơi bức tranh sẽ xảy ra:

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

Thực hiện MouseMove:

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

Thực hiện 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; 
     } 
    } 

Thực hiện 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); 
       } 
      } 
     } 
    } 

Cảm ơn rất nhiều sự giúp đỡ nào bạn có thể cung cấp.

Trả lời

11

Bạn có thể khóa các dữ liệu bitmap và gợi ý sử dụng để tự thiết lập các giá trị. Nó nhanh hơn nhiều. Mặc dù bạn sẽ phải sử dụng mã không an toàn.

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

Cảm ơn vì điều này. Tôi nghĩ rằng tôi thấy những gì đang xảy ra; ptr là một khối dữ liệu bitmap chứa các giá trị pixel màu đỏ, xanh lá cây và xanh lam và được đặt theo cách thủ công. Tôi sẽ thực hiện việc này ngay hôm nay và xem cách hoạt động. Cảm ơn rất nhiều vì điều này, tôi cảm thấy sự hiểu biết của mình đã nâng cao một khía cạnh khác: P Chỉ một điều, tôi nghĩ bitmap trong C# cũng có kênh alpha. Tôi nghĩ rằng điều này sẽ được xử lý trong thông tin màu pixel cùng với RGB. – Pyro

+1

Bạn cần chỉ định http://msdn.microsoft.com/en-us/library/system.drawing.imaging.pixelformat.aspx để bao gồm thành phần alpha. Chỉ cần thay đổi 'PixelFormat.Format24bppRgb' thành' PixelFormat.Format32bppArgb'. Tôi tin rằng nó nên được sau khi R vì vậy chỉ cần bù đắp nó bằng cách +3. Nếu bạn sử dụng 32 bit cho mỗi pixel, bạn sẽ cần phải thay đổi 'x * 3' thành' x * 4' vì nó sẽ là 4 byte cho mỗi pixel ngay bây giờ. – Jack

+0

Tôi vừa thử điều này. Nó là đáng yêu và nhanh chóng trên tất cả các kích cỡ bàn chải! Tuy nhiên hiện nay có một số hành vi kỳ lạ: Bản vẽ thực tế xảy ra ở mức bù cho vị trí của chuột. Tôi tự hỏi có liên quan gì đến dòng này không: BitmapData data = bmp.LockBits (Hình chữ nhật mới (areaToPaint.Right, areaToPaint.Bottom), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); Cần có vị trí được chỉ định cho hình chữ nhật. Tôi đã sử dụng giá trị 'vị trí' được cung cấp. Có đúng không? – Pyro

0

Chỉ cần một ý tưởng: Điền vào một bitmap offscreen với các điểm ảnh Brush của bạn. Bạn chỉ cần tạo lại bitmap này khi bàn chải, kích thước hoặc màu sắc được thay đổi. Và sau đó chỉ cần vẽ bitmap này vào bitmap hiện có của bạn, nơi con chuột được đặt. Nếu bạn có thể điều chỉnh bitmap bằng màu, bạn có thể đặt các pixel theo thang độ xám và điều chỉnh nó bằng màu cọ hiện tại.

+0

Điều đó nghe có vẻ như một ý tưởng khá hay. Cảm ơn. Tôi cho rằng nếu tôi sử dụng tính minh bạch thì nó sẽ hoạt động khi vẽ trên các đường hiện có. – Pyro

5

SetPixel thực hiện điều này: là khóa toàn bộ hình ảnh, thiết lập pixel và mở nó

cố gắng để làm điều đó: bạn có được một khóa cho hình ảnh bộ nhớ toàn bộ với LockBits, quá trình bạn cập nhật và phát hành các khóa sau .

lockbits

+0

Vì vậy, tôi khóa bitmap, làm mã vẽ của tôi và sau đó mở khóa nó? – Pyro

+0

tôi tạo cho bạn một ví dụ, một khoảnh khắc ... – fixagon

+18

3 năm sau, vẫn không có ví dụ –

3

Tôi thường sử dụng một mảng để đại diện cho dữ liệu pixel nguyên bản. Và sau đó sao chép giữa mảng đó và bitmap với mã không an toàn.

Làm cho mảng Color là một ý tưởng tồi, vì cấu trúc Color tương đối lớn (12 byte +). Vì vậy, bạn có thể xác định cấu trúc 4 byte của riêng bạn (đó là cấu trúc mà tôi đã chọn) hoặc chỉ cần sử dụng một mảng int hoặc byte.

Bạn cũng nên sử dụng lại mảng của mình, vì GC trên LOH có xu hướng đắt tiền.

Mã của tôi có thể được tìm thấy tại địa chỉ:

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

Một thay thế được viết tất cả mã của bạn sử dụng con trỏ vào bitmap trực tiếp. Đó là một chút nhanh hơn, nhưng có thể làm cho mã xấu hơn và dễ bị lỗi hơn.

+1

Bạn có sử dụng mảng có kích thước bitmap đích hay chỉ kích thước của khu vực sẽ được sửa đổi? – Pyro

+0

Trong ứng dụng của tôi, tôi đã sử dụng một mảng có cùng kích thước với bitmap, nhưng tôi cần làm việc trên toàn bộ bitmap. Nhưng ngay cả khi tôi chỉ làm việc trên một phần của bitmap tôi vẫn sẽ sử dụng mảng đầy đủ, nhưng có thể chỉ điền vào một phần của nó tôi cần. Bằng cách đó tôi không cần phải phân bổ một mảng mới khi làm việc trên một phần có kích thước khác nhau của hình ảnh. – CodesInChaos

0

Bạn đang gọi GetBitmap trong vòng lặp lồng nhau của bạn. Có vẻ như đó là không cần thiết, bạn nên GetBitmap bên ngoài vòng lặp vì tham chiếu sẽ không thay đổi.

Ngoài ra nhìn vào @fantasticfix câu trả lời, LockBits gần như luôn luôn sắp xếp vấn đề hiệu suất chậm với nhận/thiết lập pixel

Các vấn đề liên quan