共用方式為


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]

Comments

  • Anonymous
    April 15, 2008
    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?

  • Anonymous
    April 15, 2008
    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).

  • Anonymous
    April 15, 2008
    The comment has been removed

  • Anonymous
    April 15, 2008
    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.

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

  • Anonymous
    April 16, 2008
    The comment has been removed

  • Anonymous
    April 23, 2008
    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){...}?

  • Anonymous
    April 23, 2008
    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

  • Anonymous
    April 24, 2008
    I bet you had a lot of fun working with Assembly :)

  • Anonymous
    April 27, 2008
    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?

  • Anonymous
    April 30, 2008
    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

  • Anonymous
    May 23, 2008
    This class has a few bugs in it.  (Mainly using width instead of stride). Because it got me started I am posting my fixed version.  This will work with bitmaps that have a stride that is not exactly = 3 * the width. 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 FastBitmap(Bitmap bitmap, bool createCopy)    {        width = bitmap.Width;        height = bitmap.Height;        if (createCopy == true)            image = new Bitmap(bitmap);        else            image = bitmap;    }    public byte[] GetAllPixels()    {        return rgbValues;    }    public void SetAllPixels(byte[] pixels)    {        rgbValues = pixels;    }    public Color GetPixel(int x, int y)    {        int blue = rgbValues[(y * bitmapData.Stride + (x * 3))];        int green = rgbValues[(y * bitmapData.Stride + (x * 3)) + 1];        int red = rgbValues[(y * bitmapData.Stride + (x * 3)) + 2];        return Color.FromArgb(red, green, blue);    }    public void SetPixel(int x, int y, Color cIn)    {        rgbValues[(y * bitmapData.Stride + (x * 3))] = cIn.B;        rgbValues[(y * bitmapData.Stride + (x * 3)) + 1] = cIn.G;        rgbValues[(y * bitmapData.Stride + (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 numBytes = bitmapData.Stride * image.Height;        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);    } }

  • Anonymous
    May 25, 2008
    The comment has been removed

  • Anonymous
    May 25, 2008
    You are right Klay.  I left in one reference to using Width where it should not have been (in the UnlockPixels() method).  This caused it to not copy all of the changed pixels back into to image. Here is the correct version of the 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 FastBitmap(Bitmap bitmap, bool createCopy)    {        width = bitmap.Width;        height = bitmap.Height;        if (createCopy == true)            image = new Bitmap(bitmap);        else            image = bitmap;    }    public byte[] GetAllPixels()    {        return rgbValues;    }    public void SetAllPixels(byte[] pixels)    {        rgbValues = pixels;    }    public Color GetPixel(int x, int y)    {        int blue = rgbValues[(y * bitmapData.Stride + (x * 3))];        int green = rgbValues[(y * bitmapData.Stride + (x * 3)) + 1];        int red = rgbValues[(y * bitmapData.Stride + (x * 3)) + 2];        return Color.FromArgb(red, green, blue);    }    public void SetPixel(int x, int y, Color cIn)    {        rgbValues[(y * bitmapData.Stride + (x * 3))] = cIn.B;        rgbValues[(y * bitmapData.Stride + (x * 3)) + 1] = cIn.G;        rgbValues[(y * bitmapData.Stride + (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 numBytes = bitmapData.Stride * image.Height;        rgbValues = new byte[numBytes];        Marshal.Copy(ptr, rgbValues, 0, numBytes);    }    public void UnlockPixels()    {        if (!locked)            return;        locked = false;        Marshal.Copy(rgbValues, 0, bitmapData.Scan0,  bitmapData.Stride * image.Height);        image.UnlockBits(bitmapData);    } }

  • Anonymous
    June 02, 2008
    Hey people... I was using both RoshanK and Stephen classes and you both missed one thing. specially RoshanK, for example: 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; } You make three references to image.Width. This is a very very slow process. If you want some optimization you could use: public void SetPixel(int x, int y, Color cIn) {  int w = image.Width;  rgbValues[(y * w + x) * 3] = cIn.B;  rgbValues[(y * w + x) * 3 + 1] = cIn.G;  rgbValues[(y * w + x) * 3 + 2] = cIn.R; } which is much faster, try it! :) but... you already have a global variable for Width. so you could use instead: public void SetPixel(int x, int y, Color cIn) {  int w = width;  rgbValues[(y * w + x) * 3] = cIn.B;  rgbValues[(y * w + x) * 3 + 1] = cIn.G;  rgbValues[(y * w + x) * 3 + 2] = cIn.R; } which is even faster. In Stephen's class he uses bitmapData.Stride which is also slow.. so i create a global variable for that and I define is when we lock the pixels, and use it just like I explained before. I added some other classes too, in order to access and set the pixels faster, so below is my version of this class. These are some common optimization stuff and they work very effectively although they aren't very intuitive. Hope it was useful! using System; using System.Runtime.InteropServices; using System.Drawing; using System.Drawing.Imaging; public class FastBitmap {    private Bitmap image;    private BitmapData bitmapData;    private int height;    private int width;    private int stride;    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 FastBitmap(Bitmap bitmap, bool createCopy)    {        width = bitmap.Width;        height = bitmap.Height;        if (createCopy == true)            image = new Bitmap(bitmap);        else            image = bitmap;    }    public byte[] GetAllPixels()    {        return rgbValues;    }    public void SetAllPixels(byte[] pixels)    {        rgbValues = pixels;    }    public Color GetPixel(int x, int y)    {        int s = stride;        int blue = rgbValues[(y * s + (x * 3))];        int green = rgbValues[(y * s + (x * 3)) + 1];        int red = rgbValues[(y * s + (x * 3)) + 2];        return Color.FromArgb(red, green, blue);    }    public byte[] GetPixelValues(int x, int y)    {        int w = width;        int s = stride;        byte[] c = new byte[3];        c[2] = rgbValues[(y * s + (x * 3))];        c[1] = rgbValues[(y * s + (x * 3)) + 1];        c[0] = rgbValues[(y * s + (x * 3)) + 2];        return c;    }    public void SetPixel(int x, int y, Color cIn)    {        int s = stride;        rgbValues[(y * s + (x * 3))] = cIn.B;        rgbValues[(y * s + (x * 3)) + 1] = cIn.G;        rgbValues[(y * s + (x * 3)) + 2] = cIn.R;    }    public void SetPixel(int x, int y, byte r, byte g, byte b)    {        int w = width;        int s = stride;        rgbValues[(y * s + (x * 3))] = b;        rgbValues[(y * s + (x * 3)) + 1] = g;        rgbValues[(y * s + (x * 3)) + 2] = r;    }    public static implicit operator Image(FastBitmap bmp)    {        return bmp.image;    }    public static implicit operator Bitmap(FastBitmap bmp)    {        return bmp.image;    }    public void LockBitmap()    {        LockBitmap(new Rectangle(0, 0, width, height));    }    private void LockBitmap(Rectangle area)    {        if (locked) return;        locked = true;        bitmapData = image.LockBits(area, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);        stride = bitmapData.Stride;        IntPtr ptr = bitmapData.Scan0;        int numBytes = bitmapData.Stride * height;        rgbValues = new byte[numBytes];        Marshal.Copy(ptr, rgbValues, 0, numBytes);    }    public void UnlockBitmap()    {        if (!locked) return;        locked = false;        Marshal.Copy(rgbValues, 0, bitmapData.Scan0, bitmapData.Stride * image.Height);        image.UnlockBits(bitmapData);    } }

  • Anonymous
    June 02, 2008
    Another thing. I found some months ago a class that was somewhat like this one, which I also changed to become faster and more useful I've added the implicit operator too (nice touch, RoshanK ;) ) I made this class completely compatible with the previous updated version of RoshanK's class, so you can just change the class type from FastBitmap to UnsafeBitmap and allow unsafe code in the project build parameters, and test it in your program. I think its a bit faster, it doesn't use Marshal, and it uses pointers. using System; using System.Collections.Generic; using System.Text; using System.Drawing; using System.Drawing.Imaging; namespace Utility {    public unsafe class UnsafeBitmap    {        Bitmap bitmap;        int width;        public int Width, Height;        BitmapData bitmapData = null;        Byte* pBase = null;        public UnsafeBitmap(Bitmap bitmap)        {            this.bitmap = new Bitmap(bitmap);            Width = bitmap.Width;            Height = bitmap.Height;        }        public UnsafeBitmap(int width, int height)        {            this.bitmap = new Bitmap(width, height, PixelFormat.Format24bppRgb);            Width = bitmap.Width;            Height = bitmap.Height;        }        public void Dispose()        {            bitmap.Dispose();        }        public Bitmap Bitmap        {            get            {                return (bitmap);            }        }        public struct PixelData        {            public byte blue;            public byte green;            public byte red;            public static bool operator ==(PixelData a, PixelData b)            {                // If both are null, or both are same instance, return true.                if (System.Object.ReferenceEquals(a, b))                {                    return true;                }                // If one is null, but not both, return false.                if (((object)a == null) || ((object)b == null))                {                    return false;                }                // Return true if the fields match:                return a.red == b.red && a.green == b.green && a.blue == b.blue;            }            public static bool operator !=(PixelData a, PixelData b)            {                return !(a == b);            }            public override bool Equals(object obj)            {                return base.Equals(obj);            }            public override int GetHashCode()            {                return base.GetHashCode();            }        }        private Point PixelSize        {            get            {                Size st = bitmap.Size;                RectangleF bounds = new RectangleF(0, 0, st.Width, st.Height);                return new Point((int)bounds.Width, (int)bounds.Height);            }        }        public void LockBitmap()        {            RectangleF boundsF = new RectangleF(0, 0, bitmap.Width, bitmap.Height);            Rectangle bounds = new Rectangle(0, 0, bitmap.Width, bitmap.Height);            width = (int)boundsF.Width * sizeof(PixelData);            if (width % 4 != 0)            {                width = 4 * (width / 4 + 1);            }            bitmapData = bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);            pBase = (Byte*)bitmapData.Scan0.ToPointer();        }        public PixelData GetPixel(int x, int y)        {            PixelData returnValue = PixelAt(x, y);            return returnValue;        }        public byte[] GetPixelValues(int x, int y)        {            byte[] c = new byte[3];            PixelData returnValue = PixelAt(x, y);            c[0] = returnValue->red;            c[1] = returnValue->green;            c[2] = returnValue->blue;            return c;        }        public void SetPixel(int x, int y, PixelData color)        {            PixelData* pixel = PixelAt(x, y);            pixel = color;        }        public void SetPixel(int x, int y, byte r, byte g, byte b)        {            PixelData pixel = PixelAt(x, y);            pixel->blue = b;            pixel->green = g;            pixel->red = r;        }        public void UnlockBitmap()        {            bitmap.UnlockBits(bitmapData);            bitmapData = null;            pBase = null;        }        public PixelData* PixelAt(int x, int y)        {            return (PixelData*)(pBase + y * width + x * sizeof(PixelData));        }        public static implicit operator Image(UnsafeBitmap bmp)        {            return bmp.bitmap;        }        public static implicit operator Bitmap(UnsafeBitmap bmp)        {            return bmp.bitmap;        }    } }

  • Anonymous
    June 27, 2008
    guys, i need to draw a random polygon that is semi-transparent. Windows Mobile does not support Color.FromArgb(). is there any other simple workaround to this??

  • Anonymous
    July 01, 2008
    hm, I did this by writing my own rasterizer in unmanaged c++ :)

  • Anonymous
    July 01, 2008
    like Tom said do yo have to take care of everything by yourself. Seme transparent means that the stuff below the polygon can be seen through it, so you can not fill the polygon with a constant color. For this you need to implement your own fill algorithm which evaluates the color of every pixel of the polygon. You could surely do this with the FastBitmap class and draw a polygon onto the FastImage - still you need to implement the filling and drawing by yourself (as long no one posts something to this) :) - but I think it can be done. If you go this way the polygon can only "see" things that are also drawn on the FastImage - it can not see through.

  • Anonymous
    July 04, 2008
    Hum... weird... I'm using either version of the class, & in LockPixels (LockBitmap in Paulo Ricca's version), after the initialisation of the bitmapData, for a 49x49 picture the bitmapData.Stride is said to be 148, but it should be 49*49=147, so I have errors in my image. :- Any idea why? Compact framework 2, VS08...

  • Anonymous
    July 22, 2008
    The comment has been removed

  • Anonymous
    August 01, 2008
    Hi guys, just a question why i get a ArgumentException when i excute this code :                Bitmap enfin = (Bitmap)image.Clone();                bitmapData = enfin.LockBits(rec, ImageLockMode.ReadOnly,PixelFormat.Format24bppRgb); Thank you

  • Anonymous
    August 05, 2008
    the only thing that I can see to improve the calculation is to change        public void LockBitmap()        { ...            if ((width & 3) != 0)            {                width = (width ^ 3) + 4;            } ... } since binary operators should be faster than division

  • Anonymous
    August 05, 2008
    I believe Le Sage wonder why groups of 4 bytes and if I remember correctly it first byte => shades of gray second byte => shades of red third byte => shades of green forth byte => shades of blue with 0 thru 255 possible shades for each

  • Anonymous
    August 05, 2008
    Great blog!  I was able to understand and I got the code to work!  Can't beat that. Keep up the great work. From an old C programmer.

  • Anonymous
    August 05, 2008
    The comment has been removed

  • Anonymous
    August 14, 2008
    @cybervedaa I'd look at P/Invoking the AlphaBlend function.  See here:  http://blogs.msdn.com/chrislorton/archive/2006/04/07/570649.aspx

  • Anonymous
    August 15, 2008
    Unfortunatly P/Invoke is slow. You are going to take a big hit for using it.

  • Anonymous
    September 15, 2008
    hi all, 1st of thanks for the lesson and the comments. one questio, how can I merge text to the bitmap? (for intace the date)

  • Anonymous
    March 17, 2009
    Hii all I thingk about. I have 2 function public void LockBit()        {            // Lock the bitmap's bits.              Rectangle rect = new Rectangle(0, 0, input_image_width, input_image_height);            bmdata = input_image.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); //bmdata = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);            int RowsByte = bmdata.Stride;//so byte tren 1 hang            int TotalSize = RowsByte * bmdata.Height;            rgbValues = new byte[TotalSize];            // Get the address of the first line.            IntPtr ptr = bmdata.Scan0;            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, TotalSize);           // MessageBox.Show(RowsByte.ToString());            //MessageBox.Show(i.ToString());        }        public void UnLockBit()        {            int TotalSize = bmdata.Stride * bmdata.Height;            //byte[] rgbValues = new byte[TotalSize];            // Get the address of the first line.            IntPtr ptr = bmdata.Scan0;            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, TotalSize);            input_image.UnlockBits(bmdata);        } and after i using LockBit();            for (int i = 0; i < rgbValues.Length-2; i++)            {                //rgbValues[i] = (byte)(255 - rgbValues[i]);                   rgbValues[i] = (byte)(rgbValues[i]/3+rgbValues[i+1]/3+rgbValues[i+2]/3);            }               UnLockBit();            pictureBox1.Refresh(); this oke and verry fast