トップ 戻るゲームプログラミング練習帳(参考資料)

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

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

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

DIBの構造

今回作成する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なら、以下のようにします。

32ビット・256×256ピクセルDIB用BITMAPINFO設定例
/* 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をマイナスにして座標系をあわせておくと、プログラムを書く時の混乱を避けることができるかもしれませんね。

DIBの表示

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

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

今回は、より汎用的なStretchDIBits()で描画してみましょう。

StretchDIBits
int StretchDIBits(
  HDC hdc,
  int XDest,
  int YDest,
  int nDestWidth,
  int nDestHeight,
  int XSrc,
  int YSrc,
  int nSrcWidth,
  int nSrcHeight,
  CONST VOID *lpBits,
  CONST BITMAPINFO *lpBitsInfo,
  UINT iUsage,
  DWORD dwRop
);
hdc描画対象のデバイスコンテキストハンドル
XDest/YDest描画位置
nDestWidth/nDestWidth描画サイズ
XSrc/YSrcDIB上の転送開始位置
nSrcWidth/nSrcWidthDIB上の転送サイズ
lpBitsDIBのピクセル列先頭アドレス
lpBitsInfoDIBBITMAPINFOのアドレス
iUsage色形式(カラーテーブルかRGBか)
dwRopラスタオペレーション

引数がたくさんありますが、「描画するDIBの位置、大きさ」と「描画対象の描画位置、描画サイズ」さえ確認しておけば、後は毎回同じところにデバイスコンテキストやピクセル列/BITMAPINFO構造体のアドレスなどを入れるだけです。
ラスタオペレーションは、とりあえずそのまま描画するSRCCOPYを指定します。

今回作成した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の操作に慣れておいてください。

プログラムソース表示 プログラムダウンロード


トップ 戻る【楽天市場】PC市場