Francesco Belladonna
Francesco Belladonna

Reputation: 11689

Faster way to fill a 160x43 byte array from colors of Bitmap class

What's the faster way to effectively fill an array of bytes where each byte represents a pixel (black or white: < 125 = black, > 125 = white) from a Bitmap class?

I used this for colored images: Better/faster way to fill a big array in C#

However now I'm looking for something different (I can even use a single color like Red to fill this, it doesn't matter is just something I should choose), because the array format changed.

Any suggestion? Actually I'm using this code, which is obviusly not the best idea

        for (int x = 0; x < LgLcd.NativeConstants.LGLCD_BMP_WIDTH; ++x)
        {
            for (int y = 0; y < LgLcd.NativeConstants.LGLCD_BMP_HEIGHT; ++y)
            {
                tmp = bmp.GetPixel(x, y);
                array[y * LgLcd.NativeConstants.LGLCD_BMP_WIDTH + x] = (byte)((tmp.R == 255 && tmp.G == 255 && tmp.B == 255) ? 0 : 255);
                //array[y * x] = (byte)0;
            }
        }

My idea was parallelizing everything (yea, 1 thread per line maybe? (or per column)), it should help I think.

EDIT:

Ok, first, I need a way to have the possibility to access different bytes of the image at the same time, Brandon Moretz is suggesting maybe the correct way to access bytes with lockbits. I would like to avoid, however, unsafe code. Does Lockbits involves necessarily unsafe code?

Second, my idea of parallelization was to use Parallel.For. This method should use the ThreadPool class, which will use an amount of threads not greater than cores of your cpu, and they are pre-allocated.

This method will be called a lot of times, so I think it's not a big trouble, because the threadpool will be used a lot after first call.

Is what I'm saying correct?

Upvotes: 1

Views: 1368

Answers (3)

Francesco Belladonna
Francesco Belladonna

Reputation: 11689

I found the answer by myself, working with lockbits and Marshal.ReadByte with a really nice and fast result:

    public void SetPixels(Bitmap image)
    {
        byte[] array = Pixels;
        var data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        Parallel.For(0, data.Height, new Action<int>(i =>
        {
            byte tmp;
            int pixel4bpp, pixelPerbpp;
            pixelPerbpp = data.Stride / data.Width;
            for (pixel4bpp = 0; pixel4bpp < data.Stride; pixel4bpp += pixelPerbpp)
            {
                tmp = (byte)((
   Marshal.ReadByte(data.Scan0, 0 + (data.Stride * i) + pixel4bpp)
 + Marshal.ReadByte(data.Scan0, 1 + (data.Stride * i) + pixel4bpp)
 + Marshal.ReadByte(data.Scan0, 2 + (data.Stride * i) + pixel4bpp)
 + Marshal.ReadByte(data.Scan0, 3 + (data.Stride * i) + pixel4bpp)
 ) / pixelPerbpp);

                array[i * data.Width + (pixel4bpp / pixelPerbpp)] = tmp;
            }
        }));
        image.UnlockBits(data);
    }

Upvotes: 1

Brandon Moretz
Brandon Moretz

Reputation: 7621

Is using "unsafe" code blocks an option? You can use LockBits on a Bitmap to get it's BitmapData, then use Scan0 & Stride properties to iterate over it.

If it's 255 colors I'm assuming a byte per pixel, so so something like:

*( ( ( byte* )bmpData.Scan0 ) + ( y * bmpData.Stride ) + x ) = (byte)((tmp.R == 255 && tmp.G == 255 && tmp.B == 255) ? 0 : 255);

Upvotes: 3

Teoman Soygul
Teoman Soygul

Reputation: 25732

General approach is to divide the image into regions then process. i.e. you can use:

Thread 1) for (int x = 0; x < LGLCD_BMP_WIDTH /2; ++x) { ... }

Thread 2) for (int x = LGLCD_BMP_WIDTH / 2; x < LGLCD_BMP_WIDTH; ++x) { ... }

where you would have two halves of the image be processed by different threads. You can divide further into 4, 8, etc. pieces as you wish. A thread per line would be excess, as thread creation overhead would overwhelm the benefits by a large margin.

Upvotes: 1

Related Questions