« Cで書いたUSB3.0用DLLをC#から呼び出して配列を渡す方法 | トップページ | C#で配列のキャストを行うには »

2020.05.10

C#で高速にビットマップ画像を描く方法

FPGAを使ったカメラなどを作る場合、USBやイーサから受信してきたデータは配列に入っているので、これを画像として表示したいわけですが、C#で高速にやる方法がわかりましたのでここに書きます。

まず、Formに表示させたい領域にPictureBoxを貼り付けます。

一番safeで正攻法なやり方は、

Bitmap img = new Bitmap(pictureBox1.Width, pictureBox1.Height);

でBitmap型のオブジェクトを作り、

for(int y = 0; y < pictureBox1.Height; y++) {
for(int x = 0; y < pictureBox1.Width; x++) {
int val = buffer[y * pictureBox1.Width + x];
img.SetPixel(x, y, Color.FromArgb(val));
}
}

でポチポチと点を打っていく方法です。当然ながら遅すぎます。

高速化するには、

  • PictureBoxとは別に、メモリ内にBitmapを作る
  • Bitmap内の画像バッファのポインタを得て、書き込む
  • PictureBoxに貼り付ける

というやり方をします。ダブルバッファです。

Formのコンストラクタなどで

private Bitmap bmp;

を宣言したら、コンストラクタなどでBitmapを作っておきます。

bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);

実際の描画ではbmpData.Scan0というプロパティを使って、画像データ配列の先頭のポインタを得て、そのポインタに対して書き込みをします。ここでもunsafeが大活躍します。

public void Draw(byte[] buffer)
{
System.Drawing.Imaging.BitmapData bmpData = bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly, bmpPixelFormat);
private Rectangle rect = new Rectangle(Point.Empty, bmp.Size);
private System.Drawing.Imaging.PixelFormat bmpPixelFormat = System.Drawing.Imaging.PixelFormat.Format32bppRgb;
IntPtr ptr = bmpData.Scan0;
unsafe
{
int* dst = (int*)ptr.ToPointer();
for (int i = 0; i < pictureBox1.Width * pictureBox1.Height; i++)
{
if (i >= buffer.Length) break;
byte val = buffer[i];
*dst++ = (val & 0xff) << 16 | (val & 0xff) << 8 | (val & 0xff);
}
pictureBox1.Image = bmp;
}
bmp.UnlockBits(bmpData);
}

unsafeを使うとだいぶんCっぽく書けるようになりますね。それに、ポインタの実体に整数を入れるという素直な代入によって、Color.FromArgbとかしなくていいので気分的に楽です。

配列にセットし終わったらpictureBox1.Image = bmp;で画像表示して終了です。

これでかなり高速に描画できるようになりました。

 

|

« Cで書いたUSB3.0用DLLをC#から呼び出して配列を渡す方法 | トップページ | C#で配列のキャストを行うには »

コメント

コメントを書く



(ウェブ上には掲載しません)




« Cで書いたUSB3.0用DLLをC#から呼び出して配列を渡す方法 | トップページ | C#で配列のキャストを行うには »