- You can download this project here.
Introduction
Calling GetPixel
function on a Bitmap
is very slow because that function:
- Lock
Bitmap
byte data - Get pixel info and create
Color
instance - 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); a_out = a_in; 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