Mit dem Befehl SetPixel der Klasse System.Drawing.Bitmap lassen sich einzelne Pixel in einem Bitmap verändern. Leider arbeitet diese Methode sehr langsam, und es ist nicht möglich sie aus mehreren Threads gleichzeitig aufzurufen. Möchte man also ein Bitmap Pixel für Pixel durch mehrere Threads berechnen lassen, so muss man die SetPixel-Aufrufe z.B. durch lock(…) Anweisungen vor zeitgleichen Aufrufen schützen. Dies macht den Geschwindigkeitsvorteil, den mehrere Threads bringen, teilweise wieder zunichte.
Abhilfe schafft die hier vorgestellte Klasse RawRgbBitmap. Sie bietet ebenfalls eine Methode namens SetPixel, welche jedoch deutlich schneller arbeitet als die der Bitmap-Klasse und zudem von mehreren Threads gleichzeitig aufgerufen werden kann. Durch den „implicit operator Bitmap“ wird ein RawRgbBitmap automatisch in ein System.Drawing.Bitmap umgewandelt, wann immer es einer System.Drawing.Bitmap-Variablen zugewiesen wird.
Und so funktioniert’s:
Der Konstruktor der Klasse RawRgbBitmap erzeugt zunächst ein Byte-Array. Anschließend wird ein neues System.Drawing.Bitmap erzeugt, dessen Bildspeicher auf genau dem gleichen Speicherbereich abgelegt wird, wie das Byte-Array. Wir können nun also alle Pixelmanipulationen direkt an dem Byte-Array vornehmen, ohne dafür das (langsame) Bitmap-Objekt zu verwenden. Da das Bitmap jedoch auf dem gleichen Speicherbereich liegt, wie das Array, wirken sich alle Änderungen im Array auch auf das Bitmap aus.
public class RawRgbBitmap :IDisposable { int width; int height; int stride; byte[] raw; GCHandle handle; Bitmap bmp; public RawRgbBitmap(int width, int height) { this.width = width; this.height = height; int validBitsPerLine = width * 24; // Jede Zeile im Array muß ein ganzes Vielfaches von einem int32 (4 Byte) sein. Dies ist also die Schrittweite pro Bildzeile im Array: stride = ((validBitsPerLine + 31) / 32) * 4; raw = new byte[stride * height]; handle = GCHandle.Alloc(raw, GCHandleType.Pinned); bmp = new Bitmap(width, height, stride, PixelFormat.Format24bppRgb, handle.AddrOfPinnedObject()); } public void SetPixel(int x, int y, Color c) { int idx = y * stride + x*3; raw[idx] = c.B; raw[idx+1] = c.G; raw[idx+2] = c.R; } public static implicit operator Bitmap(RawRgbBitmap rbmp) { return rbmp.bmp; } public void Dispose() { bmp.Dispose(); handle.Free(); } }