Welcome to MSDN Blogs Sign in | Join | Help

Windows Mobile Team Blog

The Official Windows Mobile Team Blog
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]

Posted: Tuesday, April 15, 2008 11:15 AM by windowsmobile
Filed under:

Comments

Kevin Daly said:

Very cool, and one of the clearest explanations I've seen of this technique.

I noticed that you declare and populate a local variable "stride" but don't seem to actually do anything with it...is that an omission that actually makes a difference or did it just turn out not to be needed?

# April 15, 2008 3:45 PM

RoshanK said:

Nice catch Kevin. The stride value is not needed there. However, it is good to use if you are manipulating smaller portions of the image - or you care about the order the pixels are loaded in. Stride returns the size in bytes of a row of pixels. It also tells you if the pixels are loaded top down or bottom up (positive stride is top down, and negative stride is bottom up IIRC).

# April 15, 2008 4:08 PM

Bruce Partington said:

are wpf namespaces available on the compact framework, i haven't had time to explore?

in wpf, bitmapsource takes raw data as input and any filters can be applied without having to use unsafe methods. however we can also expose the underlying wic calls via unsafe methods if needed. using the byte array for processing is very fast and rendering in wpf is also high performance as well.

# April 15, 2008 5:01 PM

Christopher Fairbairn said:

Bruce, WPF isn't available on the Windows CE or Windows Mobile platform(s) and hence the Compact Framework doesn't support it.

Silverlight support for Windows Mobile however is aiming for public preview later this year.

# April 15, 2008 5:50 PM

ramond said:

Marshal.Copy is very expensive, why don't you store the pointer in an instance variable instead of copying all of the data.

# April 15, 2008 6:42 PM

Matt said:

More posts like this would be much appreciated.  Any tricks and improvements that developers don't even realize exist - that's just the kind of thing I need to see.  I'll keep subscribing to this RSS feed, just keep coming with the optimized code!

# April 16, 2008 11:07 AM

Bob said:

Great article!  However, it seems your doing some double check locking on your LockPixels and UnlockPixels methods.  Would it not be better to either run synchronized ([MethodImpl(MethodImplOptions.Synchronized)]) or lock(this){...}?

# April 23, 2008 2:54 PM

panefsky said:

Very informing. thanx

Please post more often here.

I am subscribed for some time now but I was expecting this to be a more active blog!

What's wrong with you people???

:) just kidding

# April 24, 2008 3:58 AM

Eltawil said:

I bet you had a lot of fun working with Assembly :)

# April 24, 2008 3:59 PM

Tom said:

Very good article, thanks.

I wonder how this code perdorms compared to the C++ variant. I guess if I could use BitBlt, etc. directly (and the Invert-Operation with it) it would be much faster - Is this image data internally a DDB oder a DIB - if it is the former one could use BitBlts directly?

# April 27, 2008 2:44 PM

Synaptime said:

Hi,

I tried this code out (VS 2005, WinMoPro6 device project)

I have a very simple setup: A PictureBox (docked full in the form) as the image container, and the PictureBox.Image = new Bitmap(screen width,screen height).

It takes about a second to invert.

(both on the emulator, and the actual device)

Any ideas why so slow?

thanks

# April 30, 2008 2:44 PM
Leave a Comment

(required) 

(required) 

(optional)

(required) 

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Page view tracker