C# Bitmap image masking using unsafe code
Asked Answered
S

1

22

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 :)

Squarerigger answered 15/9, 2011 at 15:35 Comment(3)
Can't you do this in one loop? It'll be a lot faster already.Thiosinamine
the second loop is run multiple times for each rectangle or transparency I apply to the bitmap. combining loops still won't produce a result as fast as using unsafe code..Squarerigger
You are correctly using the Width and Height properties of your BitmapData object inside your loops. Accessing these properties on the Bitmap itself would slow the code down (this is a common mistake in .NET image processing).Oyez
S
1

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;
        }
    }
}
Schroer answered 28/8, 2019 at 2:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.