Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Bitmap image masking using unsafe code

Tags:

c#

bitmap

unsafe

I'm using the following code to make image masks in C#:

for(int x = 0; x < width; x++)
{
    for(int y = 0; y < height; y++)
    {
        bmp.SetPixel(x,y,Color.White);
    }
}

for(int x = left; x < width; x++)
{
    for(int y = top; y < height; y++)
    {
        bmp.SetPixel(x,y,Color.Transparent);
    }
}

But it's WAY too slow... What is the unsafe equivalent to this? Will it be allot faster?

In the end I do a bmp.Save() in PNG format.

UPDATE:

After reading through [Link removed, dangerous site] as suggested by MusiGenesis, I made it work using the following code (for anyone who needs it):

Bitmap     bmp = new Bitmap(1000,1000,PixelFormat.Format32bppArgb);
BitmapData bmd = bmp.LockBits(new Rectangle(0, 0, bmp.Width,bmp.Height), 
                                  System.Drawing.Imaging.ImageLockMode.ReadWrite, 
                                  bmp.PixelFormat);

int PixelSize=4;

unsafe 
{
    for(int y=0; y<bmd.Height; y++)
    {
        byte* row=(byte *)bmd.Scan0+(y*bmd.Stride);

        for(int x=0; x<bmd.Width; x++)
        {
            row[x*PixelSize]     = 0;   //Blue  0-255
            row[x*PixelSize + 1] = 255; //Green 0-255
            row[x*PixelSize + 2] = 0;   //Red   0-255
            row[x*PixelSize + 3] = 50;  //Alpha 0-255
        }
    }
}

bmp.UnlockBits(bmd);

bmp.Save("test.png",ImageFormat.Png);

Alpha channel: 0 being fully transparent, 255 being no transparency on that pixel.

I'm sure you can easily modify the loop for painting a rectangle :)

like image 345
Tom Avatar asked Sep 15 '11 15:09

Tom


1 Answers

You can still speed up your loop by quite a lot, by doing fewer write operations.

Create a single row of bytes in a buffer, and ask the .net runtime to copy those to managed memory. This is roughly 10-15 times faster on my computer.

// Build a "row" array and copy once for each row // You can also build the full byte array and do only one // call to Marshal.Copy, but that of course takes more memory.  var bytesPerPixel = 4; byte[] row = new byte[bmp.Width * bytesPerPixel]; for (var i = 0; i < bmp.Width; ++i) {     var offset = i * bytesPerPixel;      row[offset+0] = 0; //Blue  0-255     row[offset+1] = 255; //Green 0-255     row[offset+2] = 0;   //Red   0-255     row[offset+3] = 50;  //Alpha 0-255 }  var bits = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadWrite, bmp.PixelFormat); for (var i = 0; i < bits.Height; ++i) {     Marshal.Copy(row, 0, bits.Scan0 + (i * bits.Stride), row.Length); } bmp.UnlockBits(bits); 

If you want to keep your "one loop iteration for each pixel", you should at least only write each pixel value once. This halves the running time on my computer.

var pixel = (uint)(a << 24) | (uint)(r << 16) | (uint)(g << 8) | (b); unsafe {     for (int y = 0; y < bmd.Height; y++)     {         var row = (uint*) ((byte*)bmd.Scan0 + (y * bmd.Stride));          for (int x = 0; x < bmd.Width; x++)         {             row[x] = pixel;         }     } } 
like image 67
gnud Avatar answered Sep 30 '22 01:09

gnud