Click here to Skip to main content
15,888,461 members
Articles / General Programming
Tip/Trick

Fast, simple Bitmap editor class

Rate me:
Please Sign up or sign in to vote.
4.82/5 (3 votes)
1 Oct 2013CPOL2 min read 11.5K   249   9  
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:

C#
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:

C#
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:

C#
    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:

C#
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:

C#
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:

C#
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Instructor / Trainer
Portugal Portugal
Hello, my name is Rui Carvalho and I'm a Math teacher in Portugal. I Studied in 'Faculdade de Ciências da Universidade do Porto' and I've been programming since I was 16 years old. I'm an algorithms expert. I started in my CASIO calculator and then moved up to Visual Basic 6 (in my lovely Pentium 233 Mhz). Then I learned JAVA and c# as soon as possible. I've been programming in c# since then. For my databases I use SQL server and MySQL. I teach JAVA, C#, T-SQL to software developers (between my Math classes).
I worked in a software company for 3 years and I loved it. I spent those 3 years changing the base routines from VB6 to C# in many applications. I learned a lot about Taxes, Billing, Payments, Production,Company Management, Server Management,Synchronization,FTP, etc...
Today, unfortunately, I'm unemployed.
(Portugal is living a very very bad economic crisis)

Comments and Discussions

 
-- There are no messages in this forum --