SetPixel太慢了。 是否有更快的方法来绘制位图?

我正在制作一个小型油漆程序。 我在位图上使用SetPixel来绘制线条。 当画笔大小变大时,如同25个像素一样,会有明显的性能下降。 我想知道是否有更快的方法来绘制位图。 以下是该项目的背景:

  • 我正在使用位图,以便我可以利用图层,如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); } } } } 

非常感谢您提供的任何帮助。

您可以锁定位图数据并使用指针手动设置值。 它要快得多。 虽然你必须使用不安全的代码。

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

SetPixel执行此操作:锁定整个图像,设置像素并解锁它

尝试这样做:使用lockbits获取整个内存映像的锁定,处理更新并释放锁定。

lockbits

我通常使用数组来表示原始像素数据。 然后使用不安全的代码在该数组和位图之间进行复制。

制作Color数组是一个坏主意,因为Color结构相对较大(12字节+)。 因此,您可以定义自己的4字节结构(这是我选择的结构),或者只使用intbyte数组。

您还应该重复使用arrays,因为LOH上的GC往往很昂贵。

我的代码可以在以下位置找到:

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

另一种方法是直接使用指针将所有代码写入位图。 这仍然有点快,但可以使代码更丑陋,更容易出错。

只是一个想法:使用画笔像素填充屏幕外的位图。 只需更改画笔,大小或颜色时,您只需重新生成此位图。 然后只需将此位图绘制到鼠标所在的现有位图上。 如果可以使用颜色调制位图,则可以将像素设置为灰度,并使用当前画笔颜色对其进行调制。

您在嵌套for循环中调用GetBitmap。 看起来没有必要,你应该在for循环之外的GetBitmap,因为引用不会改变。

另外看看@fantasticfix答案,Lockbits几乎总是通过获取/设置像素来​​排序性能问题