Faster C#

Hi everybody, my name is Roshan Khan and I’m a dev working on Windows Mobile at MS. I’ve been working at the Mobile Devices division for the past 18 months. For the majority of my day I’m working in C++ and on a few unfortunate occasions assembly. Whenever I get the chance however, I try to push managed code. In fact, you may be surprised to know that with Windows Mobile 6.1 we are shipping a feature written entirely in managed code (guess which one!).

While pushing my vigilante managed code agenda I’ve run headfirst into the limitations of the Compact Framework. The CF team did a great job getting ~30% of the functionality into ~10% of the space. And with a little bit of work you can get around many of the obstacles presented by the reduced code! And hopefully this article shows you one or two ways I managed to do this.

Today’s lesson is fast bitmap manipulation. Trying to alter pixels on an image is an intensive operation due to the problems associated with accessing managed memory. It’s just plain slow. This won’t be a big concern if all you are doing is drawing an image to a graphics object – but when you want to run a per-pixel filter on an image you quickly hit a performance bottleneck.

For this tutorial lets create a few classes that will let us invert the pixels on an image. The first thing we need to do is create a faster bitmap class. I’m going to make a class called FastBitmap, but I can’t subclass from Bitmap since it’s sealed. Oh no! Relax … we can fix this. Let’s create a class that contains a member variable that is a bitmap.

    public class FastBitmap

    {

        private Bitmap image;

 

But with this we won’t be able to pass in our FastBitmap into methods that accept Bitmaps – this is going to be a problem. One way to solve this is to simply pass in FastBitmap.image into the places that want a Bitmap, but this is a suboptimal solution. Instead let’s use the power of implicit casting!

        public static implicit operator Image(FastBitmap bmp)

        {

            return bmp.image;

        }

        public static implicit operator Bitmap(FastBitmap bmp)

        {

            return bmp.image;

        }

With this code we can now cast a FastBitmap object to an Image or Bitmap object on the fly. This helps especially when plugging the FastBitmap class into an existing application that is already using Bitmaps.

 

But we still haven’t solved our performance problems. How do we quickly manipulate the pixels on an image? Simple – lock the pixels in memory so we can access them quickly and in a contiguous fashion. This is done via the LockBits method. I’m going to add the following methods to our FastBitmap class that allow us to put the managed pixel data into a format that is better suited for direct manipulation.

        public void LockPixels()

        {

            LockPixels(new Rectangle(0, 0, image.Width, image.Height));

        }

        private void LockPixels(Rectangle area)

        {

            if (locked)

                return;

            locked = true;

            bitmapData = image.LockBits(area, ImageLockMode.ReadWrite,

                PixelFormat.Format24bppRgb);

            IntPtr ptr = bitmapData.Scan0;

            int stride = bitmapData.Stride;

            int numBytes = image.Width * image.Height * 3;

            rgbValues = new byte[numBytes];

            Marshal.Copy(ptr, rgbValues, 0, numBytes);

        }

        public void UnlockPixels()

        {

            if (!locked)

                return;

            locked = false;

Marshal.Copy(rgbValues, 0, bitmapData.Scan0, image.Width * image.Height * 3);

            image.UnlockBits(bitmapData);

            locked = false;

     }

 

By calling lock pixels we can copy the pixel data from memory into an array that we will manipulate later on. When we call UnlockPixels we dump this entire array back into our managed image in one atomic operation.

 

I’m going to ahead a few more methods to our FastBitmap class for ease of use. Here’s the completed class.

    public class FastBitmap

    {

        private Bitmap image;

        private BitmapData bitmapData;

        private int height;

        private int width;

        private byte[] rgbValues;

        bool locked = false;

        public int Height

        {

            get

            {

                return this.height;

            }

        }

        public int Width

        {

            get

            {

                return this.width;

            }

        }

        public FastBitmap(int x, int y)

        {

            width = x;

            height = y;

            image = new Bitmap(x, y);

        }

        public byte[] GetAllPixels()

        {

            return rgbValues;

        }

        public void SetAllPixels(byte[] pixels)

        {

            rgbValues = pixels;

        }

        public Color GetPixel(int x, int y)

        {

            int blue = rgbValues[(y * image.Width + x) * 3];

            int green = rgbValues[(y * image.Width + x) * 3 + 1];

            int red = rgbValues[(y * image.Width + x) * 3 + 2];

            return Color.FromArgb(red, green, blue);

        }

        public void SetPixel(int x, int y, Color cIn)

        {

            rgbValues[(y * image.Width + x) * 3] = cIn.B;

            rgbValues[(y * image.Width + x) * 3 + 1] = cIn.G;

            rgbValues[(y * image.Width + x) * 3 + 2] = cIn.R;

        }

        public static implicit operator Image(FastBitmap bmp)

        {

            return bmp.image;

        }

        public static implicit operator Bitmap(FastBitmap bmp)

        {

            return bmp.image;

        }

       

        public void LockPixels()

  {

            LockPixels(new Rectangle(0, 0, image.Width, image.Height));

        }

        private void LockPixels(Rectangle area)

        {

            if (locked)

                return;

            locked = true;

            bitmapData = image.LockBits(area, ImageLockMode.ReadWrite,

                PixelFormat.Format24bppRgb);

            IntPtr ptr = bitmapData.Scan0;

            int stride = bitmapData.Stride;

            int numBytes = image.Width * image.Height * 3;

            rgbValues = new byte[numBytes];

            Marshal.Copy(ptr, rgbValues, 0, numBytes);

        }

        public void UnlockPixels()

        {

            if (!locked)

                return;

        locked = false;

            Marshal.Copy(rgbValues, 0, bitmapData.Scan0, image.Width * image.Height * 3);

            image.UnlockBits(bitmapData);

        }

    }

With the class completed we can move on to the inversion algorithm. This part is actually quite simple.

 

        public new static void DoFilter(FastBitmap image)

        {

            image.LockPixels();

            byte[] pixels = image.GetAllPixels();

            for (int i = 0; i < pixels.Length; i++)

            {

                pixels[i] = (byte)(255 - pixels[i]);

            }

            image.SetAllPixels(pixels);

            image.UnlockPixels();

        }

Now we have an algorithm that in union with our FastBitmap class can invert a 320x240 image nearly instantly on a mobile device.

Thanks for reading. Let me know what you thought about this by leaving a comment!

[Author:Roshan Khan]