I used to manipulate images directly in memory. With C#, image manipulation, or at least pixel manipulation, is not so simple. Or you switch to unsafe code, probably having problems with untrusted environments, or you use the Bitmap's
SetPixels, which are really slow. I then decided to create the
ManagedBitmaps, which I already presented in an article. The untrusted problem is still there, as the DLL uses unsafe code, but if the DLL is trusted, everything is OK, as its users don't need to use unsafe code... but, that was not enough. The classes, as they were presented, were not helping me... so I recreated them.
Earlier, I created
ColorBitmap, which had all pixels as
Color type, and methods to copy to and from
ArgbBitmap which was another version of
ColorBitmap. But the pixels were seen as 32-bit
int values and
GrayscaleBitmap, which used bytes to represent color indexes/intensity.
This worked but when I decided to create a simple life simulation program, I wanted: To use Red as Strength, Green as Speed, Blue as Resistance... and to have another characteristic in the pixel, which couldn't be the "alpha" value. To make this work, I was forced to create another copy of the bitmap, with my own data-type... and I was about to give up the
ManagedBitmaps... so, I thought: Is it possible to use a "bidimensional array", unsafe code only to do the block copy to and from, and delegates to do the conversion from colors to the real data-types? And, after some tests, I discovered, it was, and the speed was still great.
So, the new class is only one:
ManagedBitmap, but generic.
You can create a
ManagedBitmap<byte> to represent indexed bitmaps,
ManagedBitmap<Color> or, as I did,
ManagedBitmap<Life>. And then, when creating system bitmaps, copying to and from them, you must simply provide a delegate to do the conversion. Fast, easy... but not enough.
One thing I didn't like in my old example is that I needed to copy data to a managed bitmap and, then, copy the data to the real bitmap. Isn't there a way to work with the bitmap directly, without using unsafe code? To be honest, that was my original idea, but for lack of tests I concluded that the switch to and from unsafe code was the real problem. But it wasn't. So, I created another class... or better, a pack of classes.
LockBitmap32 locks a 32-bit bitmap in memory and allows to access its pixels. This class uses unsafe code but, when you use it, all bound-checking is done, so there is nothing unsafe in using it. Plus two additional versions of this class, one which is read-only and another that is write-only... Plus the same classes for 8-bit and 24-bit bitmaps. In my tests, still slower than the full unsafe code, but about 35x faster than using
So, How do the Classes Work?
To present the classes, I will need to separate the
ManagedBitmap and the "
Lock" classes. So, I will start with the
Lock classes are, in fact, very simple. When creating them, you need to pass a
System.Drawing.Bitmap as a parameter. During construction, a call to the
LockBits will be made for the entire bitmap or for the Rectangle you specify, using the read/write appropriate and checking if the
PixelFormat is correct. The finalizer and the
Dispose will call
UnlockBits in the
BitmapData obtained. The indexers will simply check the boundaries and, if they are ok, get or set the value directly.
Very simple, isn't it?
Want some code? So, this is a piece of
PfzDrawingSample. It will use the source-bitmap unchanged, and will always generate a new destination-bitmap with the given red, green and blue changes over the source-bitmap.
using(var sourcePixels = new LockBitmapRgb24Read(fOriginalBitmap.AsSystemBitmap))
using (var destPixels = new LockBitmapRgb24Write(fBitmap.AsSystemBitmap))
for (int y=0; y<height; y++)
for(int x=0; x<width; x++)
Rgb24 color = sourcePixels[x, y];
byte r = p_Calculate(color.Red, trackBarRedValue);
byte g = p_Calculate(color.Green, trackBarGreenValue);
byte b = p_Calculate(color.Blue, trackBarBlueValue);
color = new Rgb24(r, g, b);
destPixels[x, y] = color;
As the original bitmap does not change, I can use the
LockRgb24Read. As the Destination bitmap is only written, but not read, I can use the
LockRgb24Write. If I was planning to use grayscale/indexed bitmaps, I could use the
for (int y=0; y<height; y++)
for(int x=0; x<width; x++)
Color color = fOriginalSystemBitmap.GetPixel(x, y);
int r = p_Calculate(color.R, trackBarRedValue);
int g = p_Calculate(color.G, trackBarGreenValue);
int b = p_Calculate(color.B, trackBarBlueValue);
fSystemBitmap.SetPixel(x, y, Color.FromArgb(r, g, b));
Except for the two
using clauses for the
lock, the code is the same. But, the
SetPixel is really slower.
ManagedBitmap is dead for the original purpose but, still, it is very useful. It is faster than the Locks, but in many cases it requires copying to and from a real bitmap, so this can be a problem. I used a
ManagedBitmap<Life> in my example of simple life simulation, as I needed extra information in the pixels. But, thanks to Karol Kolenda, I can say the
ManagedBitmap is now really useful. If you are generating the bitmap, you can use one of the specialized
Indexed8Bitmap [in general, for grayscale images]). These two
ManagedBitmap classes also have a property called "
AsSystemBitmap". The advantage of such property is that it is a
System.Drawing.Bitmap built directly from the
ManagedBitmap array. So, changes to this bitmap will be reflected in the
ManagedBitmap (so you can use all Graphics functions) and also changes to the pixels will automatically be reflected in such system bitmap. Good performance and the possibility to use GDI+ graphics. Is something more needed?
How do I transform a
Life instance into a
if (life == null)
return new Rgb24();
return new Rgb24((byte)life.Strength, (byte)life.Speed, (byte)life.Resistence);
The code is small and, now, simple. I create a Rgb24 representation using the Strength as Red, Speed as Green and Resistance as Blue.
Well, that's it.
In the future, I want to create some classes to load images (be they simple .bmp or complex .jpg) directly into
ManagedBitmaps, without the need to go to unmanaged code. But, I must warn you, the actual classes may get a lot of new methods and some of them may have breaking-changes.
- 8th February, 2010
- 3rd March, 2010
Indexed8Bitmap, which have a
System.Drawing.Bitmap representation without the need to copy
- Also made the
Lock classes more general, so the
LockBitmap32 can work with any 32-bit pixel format
- 11th March, 2010
- Added 24-bit bitmap support and changed the samples to use them
- Corrected a bug in some
CopyFrom methods, where the
X index was ignored