Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Fast, simple Bitmap editor class

0.00/5 (No votes)
1 Oct 2013 1  
Fast and simple Bitmap processor...
  • You can download this project here.

Introduction

Calling GetPixel function on a Bitmap is very slow because that function:

  1. Lock Bitmap byte data
  2. Get pixel info and create Color instance
  3. Unlock Bitmap byte data

That's a very slow routine because locking and unlocking bytes from a Bitmap is very slow. Now imagine doing that for each pixel in a big image. But that's not all, because we still need to create the Color structure instance and that's even worse because it verifies if that color is a system color or a web color, it verifies the name of the color (if it has one) and that is very very slow. If only we needed that information but 99% of the times we don't care about the name of the color or if it is a system/web color. We only want to know the raw byte information about that particular color. This class solves that problem.

Using the code

The principal class in the project is BitmapEditor class and it makes everything for you. You just need to make a ProcessPixel (delegate) compliant method that does whatever you want to each pixel and then call it as many times you want. This is what the delegate looks like:

public delegate bool ProcessPixel(byte a_in, byte r_in, byte g_in, 
     byte b_in, out byte a_out, out byte r_out, out byte g_out, out byte b_out); 

And here is an example of an implementation of a processing method:

bool convertToGrayScale(byte a_in, byte r_in, byte g_in, byte b_in, 
          out byte a_out, out byte r_out, out byte g_out, out byte b_out)
{
     byte avg = (byte)((r_in + g_in + b_in)/3);//Average of colors...
     a_out = a_in; // alpha channel (tranparency) stays the same...
     r_out = avg;
     g_out = avg;
     b_out = avg;
     return true;
}

This last method converts (as the name implies) our Bitmap to gray scale.

If you want you can make a simple color structure to simplify the writing a bit you could do something like this:

    public struct SimpleColor
{
    public SimpleColor(byte alpha, byte red, byte green, byte blue)
    {
        this.Alpha = alpha;
        this.Red = red;
        this.Green = green;
        this.Blue = blue;
    }
    public byte Alpha;
    public byte Red;
    public byte Green; 
    public byte Blue;
}

Then the delegate should be:

public delegate bool ProcessPixel(SimpleColor color_in,out SimpleColor color_out); 

The Code Behind

This is the code behind the processing method of the class:

void ProcessBitmapBytes(ProcessPixel function)
{
    if (!locked)
        throw new Exception("Bitmap data hasn't been blocked!");
 
    unsafe
    {
        for (int i = 0; i < bmpd.Height; i++)
        {
            byte* row = (byte*)bmpd.Scan0 + (i * bmpd.Stride);
            int current = 0;
 
            for (int j = 0; j < bmpd.Width; j++)
            {
                byte b = row[current];
                byte g = row[current + 1];
                byte r = row[current + 2];
                byte a = row[current + 3];
 

                byte ao, ro, go, bo;
 
                if (function(a, r, g, b, out ao, out ro, out go, out bo))
                {
                    row[current] = bo;
                    row[current + 1] = go;
                    row[current + 2] = ro;
                    row[current + 3] = ao;
                }
 
                current += 4; 
            }
        }
    }
}

I created a pointer to each row of the Bitmap and call the given ProcessPixel method for each pixel. Then, if the function succeeds I update the pixel data via pointer. This works fine and processes an image with 800x600 pixels in about 50 milliseconds (depends on the PC).

You can call any ProcessPixel you like if you use the public method ProccessBitmap. This method locks Bitmap data, processes the image an then unlocks the Bitmap data. Here is the code:

public void ProccessBitmap(params ProcessPixel[] functions)
{
     LockData();
     for (int i = 0; i < functions.Length; i++)
         ProcessBitmapBytes(functions[i]);
     UnlockData();
}

History

  • No changes were made.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here