I have a small paint program that I am working on. I am using SetPixel on a bitmap to do that drawing of lines. When the brush size gets large, like 25 pixels across there i
SetPixel does this: is locks the whole image, sets the pixel and unlocks it
try to do that: you acquire a lock for the whole memory image with lockbits, process you update and release the lock after.
lockbits
I usually use an array to represent the raw pixel data. And then copy between that array and the bitmap with unsafe code.
Making the array of Color
is a bad idea, since the Color
struct is relatively large(12 bytes+). So you can either define your own 4 byte struct(that's the one I chose) or simply use an array of int
or byte
.
You should also reuse your array, since GC on the LOH tends to be expensive.
My code can be found at:
https://github.com/CodesInChaos/ChaosUtil/blob/master/Chaos.Image/
An alternative is writing all your code using pointers into the bitmap directly. That's a bit faster still, but can make the code uglier and more error prone.
You're calling GetBitmap within your nested for loop. It looks like that's not necessary, you should GetBitmap outside the for loops as the reference isn't going to change.
Also look at @fantasticfix answer, Lockbits nearly always sorts slow performance issues with getting/setting pixels
Just an idea: Fill an offscreen bitmap with your Brush pixels. You only need to regenerate this bitmap when the brush, size or color is changed. And then just draw this bitmap onto your existing bitmap, where the mouse is located. If you can modulate a bitmap with a color, you could set the pixels in grayscale and modulate it with the current brush color.
You can lock the bitmap data and use pointers to manually set the values. It's much faster. Though you'll have to use unsafe code.
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);
}