DWORD境界の脅威

相変わらずBitmap読んでるんですが(ぉ^^;*1 )、従来のコードにバグみたいなものがあることに気づいたわけです。

仮に24bitBitmapのデータ本体を読み込むとした時…

BITMAPHEADERINFO sInfo; // + どうにかする
void*            pBuff; // |
DWORD            dwReadSize;
const UINT       nBMPSize =
  sInfo.biWidth * abs(sInfo.biHeight) * sInfo.biBitCount / 8; 

ReadFile(hBMPFile, pBuff, nBMPSize, &dwReadSize);

と描いては正常に読めない事は有名どころ。横幅は 4byte の倍数として記録されているため、ごみバイトを考慮してないわけですねー。

というわけで、こんな感じのコードを描いてたわけです。

BITMAPHEADERINFO sInfo; // + どうにかする
void*            pBuff; // |
DWORD            dwReadSize;
const UINT       nWidthDWORD = (sInfo.biWidth + 3) & ~0x03;
const UINT       nBMPSize =
  nWidthDWORD * abs(sInfo.biHeight) * sInfo.biBitCount / 8; 

ReadFile(hBMPFile, pBuff, nBMPSize, &dwReadSize);

これであれば、width が 4の倍数になるので良い感じ。 うむ。
…と思ったのですが、読み取ったデータを利用する時に大問題があることに気づいたわけです。

// 24bit bmp だとした場合
const int  nPixelSize = sInfo.biWidth * abs(sInfo.biHeight);

RGBTRIPLE* psData    = static_cast<RGBTRIPLE*>(pBuff);
RGBTRIPLE* psDataEnd = psData + nPixelSize;

for(; psData != psDataEnd; ++psData)
{
  psData->rgbRead; // とかごにょごにょ…
}

……

// 24bit bmp, w:h = 2:3 だった場合
bgrbgrxx
bgrbgrxx
bgrbgrxx

↓バイト列だと

bgrbgrxxbgrbgrxxbgrbgrxx

psData[2].rgbRed = x; psData[2].rgbGreen = x; じゃん! orz

つか、それ以前にゴミを考慮してないから、普通に ++ していくだけじゃ扱うことできないやん……。

というわけで 「1行分のデータを読む -> ゴミを読みすてる -> 1行分のデータを読む -> ゴミを読みすてる」 という形で、ゴミデータを読み捨てる方法で、データ本体だけの純粋な配列にすることにしました。

…が、8bit 以上であればコレでよいのですが、問題は4bit以下のビットマップでして…。 0.5byte の世界は激しくめんどくさいヨ orz

*1:コード打つ時間が中々ないのよ…