これまで、何度もビットマップファイルを読み込みDIBを作成してきました、基本的に、ビットマップファイル(BMPファイル)のビット列とメモリ上のDIBとでは先頭のヘッダが僅かに違うだけなので、読み込む時には先頭部分をのぞいてメモリに読み込めばそのままDIBになります。今回は、逆にメモリ上のDIBをビットマップファイルにしてみました。処理としては、ヘッダの調整程度なのでそれほど難しくはありません。
ビットマップファイルの構造
フルカラービットマップファイルの構造は、以下のようになっています。
- BITMAPFILEHEADER
- BITMAPINFOHEADER
- ビットマップのピクセル列
このうちBITMAPINFOHEADERは、BITMAPINFOからカラーテーブルの先頭アドレスをのぞいたものです(つまり4バイト小さい。この差を忘れるとハマるので注意!)。BITMAPFILEHEADERは、すでに何度も扱ってきましたね。ここには、先頭にビットマップファイルである事を示す'BM'の2バイトのほか、ピクセル列までのオフセットなどが記されています。
これらのヘッダの内、最低限指定する必要があるのはBITMAPFILEHEADERの先頭2バイトとオフセット、ファイルサイズ、それにBITMAPINFOHEADERのビットマップのサイズと形式のようです。他の情報は0にしておいても大丈夫のようなので、まず必要な大きさのバッファをゼロクリアして確保すれば良いでしょう。
ファイルを作成する時には、メモリ上にファイルのイメージを作ってそれをファイルに書き出す事にします。ファイルにはヘッダとピクセル列を書き出すので、必要なメモリ容量はビットマップの横1ラインの長さをdwLengthバイト(4バイト境界に注意)、高さをdwHeightピクセルとすれば以下のようになります。
dwLength×dwHeight+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)
メモリイメージの作成は、BITMAPFILEHEADERを設定したら、書き出すDIBのBITMAPINFOのbiWidth, biHeightを見てBITMAPINFOHEADERで大きさを設定します。あるいは、単純に書き出すDIBのBITMAPINFOをメモリのBITMAPINFOHEADERにコピーしても良いかもしれませんね。
ヘッダが出来たら、後はピクセル列をコピーしてファイルに書き出すだけです。
以上の事をまとめると、ファイル名lpszFnにDIB(BITMAPINFO-lpInfo、 ピクセル列-lpPixel)を書き出す関数は以下のようになります。
void saveBMP(LPBYTE lpszFn,LPBITMAPINFO lpInfo,LPBYTE lpPixel) {
LPVOID lpBufl;
LPBITMAPFILEHEADER lpHead;
LPBITMAPINFOHEADER lpInfol;
LPBYTE lpPixell;
DWORD dwWidth,dwHeight,dwLength,dwSize;
HANDLE fh;
/* ビットマップの大きさ設定 */
dwWidth=lpInfo->bmiHeader.biWidth;
dwHeight=lpInfo->bmiHeader.biHeight;
if ((dwWidth*3) % 4==0) /* バッファの1ラインの長さを計算 */
dwLength=dwWidth*3;
else
dwLength=dwWidth*3+(4-(dwWidth*3) % 4);
lpBufl=GlobalAlloc(GPTR, /* メモリ確保 */
sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwLength*dwHeight);
/* メモリイメージ用ポインタ設定 */
lpHead=(LPBITMAPFILEHEADER)lpBufl;
lpInfol=(LPBITMAPINFOHEADER)((LPBYTE)lpBufl+sizeof(BITMAPFILEHEADER));
lpPixell=(LPBYTE)lpBufl+sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
/* ビットマップのヘッダ作成 */
lpHead->bfType='M'*256+'B';
lpHead->bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+dwLength*dwHeight;
lpHead->bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
lpInfol->biSize=sizeof(BITMAPINFOHEADER);
lpInfol->biWidth=dwWidth;
lpInfol->biHeight=dwHeight;
lpInfol->biPlanes=1;
lpInfol->biBitCount=24;
/* ピクセル列をコピー */
CopyMemory(lpPixell,lpPixel,dwLength*dwHeight);
fh=CreateFile(lpszFn,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
WriteFile(fh,lpBufl,sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFO)+dwLength*dwHeight,&dwSize,NULL);
CloseHandle(fh);
if (dwSize<sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFO)+dwLength*dwHeight)
MessageBox(hwnd,"ファイル作成に失敗しました。空き容量を確認してください",
"書き込みエラー",MB_OK);
return;
}
プログラム
まず、適当なフルカラービットマップファイルをドロップして読み込んでください。次に、「緑色化」ボタンをクリックすると、ビットマップが緑色の淡色スケールに変換され「保存」ボタンで現在表示されているビットマップを保存します。