ビットマップイメージに独自のフィルタをかける

ビットマップの各ピクセルに何らかの操作を行いたい場合、C#では GetPixel, SetPixel メソッドが使えます。 しかし、この方法では大量のピクセルを扱う場合、処理が重くなる欠点があります。 効率よく処理するには、以下の例のように Bitmap をシステム メモリにロックしてから行います。
unsafe コードを使用しますので、プロジェクトの設定で「アンセーフ コードの許可」が必要です。
// 白抜き画像に反転する。ソース画像は黒字に白で描画されていることを前提にする
public Bitmap InvertImage(Bitmap source, int color)
{
    Bitmap output = new Bitmap(source.Width, source.Height, PixelFormat.Format32bppArgb);
    var rect = new Rectangle(0, 0, source.Width, source.Height);
    var bitsInput = source.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
    var bitsOutput = output.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
    byte R = (byte)((color >> 16) & 0xff);
    byte G = (byte)((color >> 8) & 0xff);
    byte B = (byte)(color & 0xff);
    unsafe
    {
        for (int y = 0; y < source.Height; y++)
        {
            byte* ptrInput = (byte*)bitsInput.Scan0 + (y * bitsInput.Stride);
            byte* ptrOutput = (byte*)bitsOutput.Scan0 + (y * bitsOutput.Stride);
            for (int x = 0; x < source.Width; x++)
            {
                int bp = x * 4;
                byte r = ptrInput[bp];
                byte g = ptrInput[bp + 1];
                byte b = ptrInput[bp + 2];
                int brightness = ((r + g + b) / 3);
                ptrOutput[bp] = B;
                ptrOutput[bp + 1] = G;
                ptrOutput[bp + 2] = R;
                ptrOutput[bp + 3] = (byte)brightness;
            }
        }
    }
    source.UnlockBits(bitsInput);
    output.UnlockBits(bitsOutput);
    return output;
}