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

时间:2011-10-14 13:57:00

标签: c# graphics bitmap gdi layer

我正在制作一个小型油漆程序。我在位图上使用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);
                }
            }
        }
    }

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

5 个答案:

答案 0 :(得分:12)

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

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

答案 1 :(得分:5)

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

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

lockbits

答案 2 :(得分:3)

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

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

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

我的代码可以在以下网址找到:

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

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

答案 3 :(得分:1)

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

答案 4 :(得分:0)

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

另请参阅@fantasticfix答案,Lockbits几乎总是通过获取/设置像素来​​排除性能问题