24ビットDIB

32ビットDIBでは、1ピクセルの情報を32ビットで保持していましたが、このうち上位8ビットは使用していませんでした。本来RGB各8ビットであれば、1ピクセルに必要なビット数は24ビット。32ビットDIBは、ピクセル列をDWORD単位で扱えるという便利さと引き換えにメモリを無駄に消費していたわけです。

 この無駄をなくしたい、というときは24ビット形式のDIBを使用することもできます。この形式では、RGB各8ビットの計24ビットで1ピクセルの情報を保持するため、メモリを効率的に利用できます。また、Windowsで一般的なフルカラーBMPファイルも通常はこの24ビット形式で保存されるので、BMPファイルとの間で画像データを読み書きしやすい、という利点もあります。ピクセルの処理はやや面倒になるものの、単に画像データを保持して表示するだけなら、32ビットより24ビット形式のDIBの方が効率的な場合もあるでしょう。

24ビットDIBの構造

 24ビットDIBの構造は、ピクセル列が24ビット単位になりBITMAPINFOHEADERbiBitCountが24になる以外は32ビットと同じです。ただし、ピクセル列内のRGBは、メモリ上ではBGRの順に並んでいるので注意してください。といっても、32ビットDIBDWORD型でアクセスしたので上位から順に未使用の8ビットに続きRGBという表現が可能でしたが、メモリ上ではBGRに未使用の最上位8ビットが続く、という構成になっていてRGBの構成としては同じなのですが(いわゆるバイトオーダーの問題)。

 ただ、DIBのピクセル列には「バッファの横幅は4の倍数バイトでなければならない」といういかにも「コンピュータの都合」的な制約があります。この制約は、もともと1ピクセル4バイト(32ビット)の32ビットDIBでは問題になりませんが、1ピクセル3バイトの24ビットDIBではビットマップの横幅が4の倍数でないときは注意しないといけません。

 実際にこの制約を受けるサイズのDIBを作成する場合は、「ビットマップの横幅×3を4の倍数に切り上げた数」を1ライン分のピクセル列の横幅にします。たとえば、ビットマップの大きさが31×31ピクセルなら、このDIBのピクセル列の横幅は、31 * 3 = 93を4の倍数単位に切り上げ96となります。そして、ピクセル列全体のメモリ容量は、96 * 31 = 2976バイトになります。

 事前に大きさを特定できない24ビットDIBの処理を書くときには、以下のように4の倍数単位に切り上げる処理を行ってDIBの横幅iWidthからピクセル列の横幅iLengthを求めると良いでしょう。

  if (iWidth % 4 == 0) {
      iLength = iWidth * 3;
  } else {
      iLength = iWidth * 3 + (4 - (iWidth * 3) % 4);
  }

 なお、「4の倍数に切り上げる」処理にはいろいろな書き方があるので、いろいろ工夫してみるのも面白いかもしれません。

 このようにしてピクセル列バッファの横幅を求めたら、後はピクセル列バッファの先頭アドレスをlpbyPixlesとすると

  r = lpbyPixels[x * 3 + y * iLength + 2]
  g = lpbyPixels[x * 3 + y * iLength + 1]
  b = lpbyPixels[x * 3 + y * iLength]

 とすればピクセル(x, y)のRGB各成分を参照できます。ピクセルを参照するのにRGBそれぞれ1バイトずつ参照していくのがやや面倒ですが、まあこれはしかたないですね。24ビットDIBを使うべき時というのは、ピクセルの処理をまったく行わないか、ピクセル単位の処理の手数を増やしてでもメモリ効率を最優先したい時、なのですから。

プログラム

 プログラムを実行すると、255×256ピクセルの24ビットDIBを作成し、(16, 31)と(16, 32)に赤い点を打ちます。ビットマップの大きさを変えてみたり、ピクセル列にいろいろ書き込んでみてください

プログラムソース表示


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