DIB(デバイス独立ビットマップ)の作成

DIB(デバイス独立ビットマップ)とは、メモリ上の任意の領域をRGB形式(またはカラーテーブルで指定される色の配列)の画像データを格納した「ビットマップ」として扱い、画面などに描画する機能です。Windowsの標準的な画像ファイル形式であるBMPファイルも、このDIBがほぼそのまま格納されています。
 DIBの画像データ(ピクセル列)は、普通に確保したバッファにRGBの値を数値データとして書き込むだけですから、「何でもあり」です。BMPファイル内の画像をメモリに読み込んで画像処理を施してから画面上に描画したり、適当な大きさの画像用バッファ(フレームバッファ)を作成しそこに各ピクセルの値を指定して好きな絵を描き画面に描画したり、あるいはゲームの画面作成に使用して多彩な画面効果を試してみるのも良いでしょう。

DIBにはいくつか種類があるのですが、今回はその中でも最も扱いやすい1ピクセル32ビット形式のフルカラーDIBを作ってみることにします。

DIBの構造とBITMAPINFOHEADER

今回作成する32ビットフルカラーDIBは、メモリ上のある領域(バッファ)を32ビット単位でRGBの配列(1ピクセル32ビットのフレームバッファ)に見立てて画面に表示するものです。このバッファは1ピクセル32ビット(4バイト)なので、必要な容量はビットマップの大きさから以下の式で求められることになります。

 横幅 × 高さ × 4バイト

たとえば、256×256ピクセルのDIBを作成するのなら、以下のようにして256×256×4バイト=256KBのバッファを確保します。

  /* DIB用ピクセル列確保 */
  g_lppxDIB = (LPDWORD)HeapAlloc(GetProcessHeap(),
               HEAP_ZERO_MEMORY, 256 * 256 * 4);

バッファを確保したら、実際に書き込んでみましょう。バッファに書き込むときは、バッファをDWORD型配列として扱うと配列内の各DWORD型要素を1ピクセルに対応させられるので便利です。DIBのピクセル列は、この配列の中に横1ラインずつ並んで(というか、積み上げられて)配置されています。座標系は、横方向が左から右、縦方向が下から上ですので、ちょうど普通のグラフと同じですね。

256×256ピクセルのDIBピクセル列バッファの先頭アドレスをDWORD型ポインタg_lppxDIBに格納したとすると、(x, y)のピクセルは以下の式で参照できます。

  g_lppxDIB[x + y * 256]

要は、各列の一番左端(0, y)のピクセルが先頭から横幅×yバイトの位置にあって、あとはx分足せばよいわけですね。バッファに書き込む値は、32ビット整数で最上位8ビットは未使用、下位24ビットが順番にRGB各8ビット、という構成になっています。ですから、たとえばこのバッファの(16, 32)に赤い点(0x00ff0000)を打つなら、以下のようにします。

	g_lppxDIB[16 + 32 * 256] = 0x00ff0000;

あとは、同様にしてバッファに画像データを書き込んでいくだけ。座標系がGDIのデフォルトの座標系とは「上下逆」になっている点に注意すれば、それほど難しくはないでしょう。

バッファに画像データを書き込んだら、続いてそのバッファをDIBピクセル列として画面に描画するためにBITMAPINFO構造体を作成します。この構造体は、DIBの色形式や大きさなどを指定するもので、画像データを書き込んだバッファとこの構造体のアドレスをDIB描画用APIに渡すことでバッファの内容をDIBとして画面に描画することができるようになります。

BITMAPINFO構造体は、実際にビットマップの形式を設定するBITMAPINFOHEADER構造体とカラーテーブル(あるいはRGBの配置を変更するためのビットフィールド)で構成されます。ただし、今回作成するRGB各8ビットの32ビットDIBではカラーテーブルは使用しないので、BITMAPINFOHEADERの部分だけを設定します。
BITMAPINFOHEADER構造体には多くのメンバがありますが、DIBとして画面に描画するには、最初に構造体の内容を0でクリアしてから構造体の大きさとビットマップの色形式・大きさを設定すれば十分です。今回作成する32ビットフルカラー、256×256ピクセルのDIBなら、以下のようにします。

  /* DIB用BITMAPINFOをクリア */
  ZeroMemory(&g_biDIB, sizeof(g_biDIB));

  /* DIB用BITMAPINFO設定 */
  g_biDIB.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  g_biDIB.bmiHeader.biWidth = 256;
  g_biDIB.bmiHeader.biHeight = 256;
  g_biDIB.bmiHeader.biPlanes = 1;
  g_biDIB.bmiHeader.biBitCount = 32;
  g_biDIB.bmiHeader.biCompression = BI_RGB;

最低限設定すべきBITMAPINFOHEADER構造体のメンバは、構造体サイズ(biSize)、ビットマップの大きさ(biWidth/biHeight)、それにピクセルの色形式(biPlanes/biBitCount/biCompression)です。このうち、高さを表すbiHeightをマイナスの値(この場合は-256)にすると、座標系を上下逆にしてGDIのデフォルトの座標系に合わせることができます。DIBGDIの描画機能を組み合わせて描画する場合には、BITMAPINFOHEADER構造体のbiHeightをマイナスにして座標系をあわせておくと、プログラムを書く時の混乱を避けることができるかもしれませんね。

以上で、ピクセル列を格納したバッファとそのバッファをどんな形式のビットマップとして扱うか指定するBITMAPINFO構造体というDIBを構成する2つの要素がそろったので、いよいよ画面に描画してみましょう。

DIBを描画するには、StretchDIBits()SetDIBitsToDevice()を使用します。両方とも描画対象のデバイスコンテキストや描画位置、描画サイズ、ピクセル列/BITMAPINFO構造体のアドレス、と指定する引数は大体似たようなものですが、StretchDIBits()は描画サイズに応じて拡大縮小されます。
 今回は、より汎用的なStretchDIBits()で描画してみましょう。

まず、ヘルプやネット上のサーチエンジンでStretchDIBitsを検索し、引数を確認してみてください。引数がたくさんありますが、「描画するDIBの位置、大きさ」と「描画対称の描画位置、描画サイズ」さえ確認しておけば、後は毎回同じところにデバイスコンテキストやピクセル列/BITMAPINFO構造体のアドレスなどを入れるだけです。

今回作成したDIB(ピクセル列g_lppxDIBBITMAPINFO構造体g_biDIB)をデバイスコンテキストhdcの(0, 0)からそのままの大きさ(256×256ピクセル)で描くには、以下のようにします。

  /* DIB描画 */
  StretchDIBits(hdc, 0, 0, 256, 256,
              0, 0, 256, 256, g_lppxDIB,
              &g_biDIB, DIB_RGB_COLORS, SRCCOPY);

プログラム

最初に256×256ピクセルの32ビットDIBを作成し、画面に描画します。DIBの大きさを変えたり、ピクセル列に適当に点を打ったりしてDIBの操作に慣れておいてください。

プログラムソース表示


プログラミング資料庫 > Windowsプログラミング研究室