ビットマップ関係の関数の引数は多いです。int SetDIBitsToDevice( HDC hdc, // デバイスコンテキストハンドル int XDest, // 転送先左上のX座標 int YDest, // Y座標 DWORD dwWidth, // 転送元の横幅 DWORD dwHeight, // 転送元の高さ int XSrc, // 転送元左上のX座標 int YSrc, // 転送元左上のY座標 UINT uStartScan, // ビットマップデータの最初の走査線番号 UINT cScanLines, // 走査線の数 CONST VOID *lpvBits, // ビットマップデータを記憶したバッファへのポインタ CONST BITMAPINFO *lpbmi, // BITMAPINFO構造体へのポインタ UINT fuColorUse // ビットマップカラーテープルの種類 );
uStartScanは最初からスキャンしたいなら0にします。
cScanLinesはビットマップの高さになります。
さて、lpbmiはどうしたらよいのでしょうか。 bmiHeaderメンバはBITMAPINFOHEADER構造体をそのままコピーします。 bmiColors[1]メンバはRGBQUAD構造体はないので放置しておきます。
最後のfuColorUseは、DIB_PAL_COLORSかDIB_RGB_COLORSの いずれかです。前者はカラーテーブル(RGBQUAD構造体の集合)に16ビットのインデックスから なる配列が入っており、論理パレットに対するインデックスになっています。 パレットについては後の章で解説します。後者はカラーテーブルに RGBの値が直接入っています。
さて、今回のプログラムはなるべく簡略化するために前章で作った ReadDIB関数にビットマップ表示用のコードを少し加えただけです。 あとは、前章と全く同じです。
ブロックで囲んだ部分が今回追加したところです。とりあえずchar型へのポインタを 定義しておいてGlobalAllocで必要な分だけメモリを確保します。メモリに読みこむのは ビットマップファイルのサイズからからBITMAPFILEHEADERの部分を除いた分です。int ReadDIB(HWND hWnd) { HANDLE hF; HANDLE hMem1, hMem2; LPBITMAPFILEHEADER lpBf; LPBITMAPINFOHEADER lpBi; DWORD dwResult; LONG wx, wy; DWORD dwFileSize, dwOffBits; WORD wBitCount; DWORD dwClrUsed, dwClrImportant; DWORD dwSizeImage; char str[256]; char szFType[3]; hF = CreateFile(szFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hF == INVALID_HANDLE_VALUE) { MessageBox(hWnd, "ファイルのオープンに失敗しました", "Error", MB_OK); return -1; } hMem1 = GlobalAlloc(GHND, sizeof(BITMAPFILEHEADER)); lpBf = (LPBITMAPFILEHEADER)GlobalLock(hMem1); ReadFile(hF, (LPBITMAPFILEHEADER)lpBf, sizeof(BITMAPFILEHEADER), &dwResult, NULL); dwFileSize = lpBf->bfSize; szFType[0] = LOBYTE(lpBf->bfType); szFType[1] = HIBYTE(lpBf->bfType); szFType[2] = '\0'; dwOffBits = lpBf->bfOffBits; wsprintf(str, "dwFileSize = %d, szFType = %s dwOffBits = %d", dwFileSize, szFType, dwOffBits); MessageBox(hWnd, str, "BMP情報その1", MB_OK); hMem2 = GlobalAlloc(GHND, sizeof(BITMAPINFOHEADER)); lpBi = (LPBITMAPINFOHEADER)GlobalLock(hMem2); ReadFile(hF, (LPBITMAPINFOHEADER)lpBi, sizeof(BITMAPINFOHEADER), &dwResult, NULL); wx = lpBi->biWidth; wy = lpBi->biHeight; wBitCount = lpBi->biBitCount; dwClrUsed = lpBi->biClrUsed; dwClrImportant = lpBi->biClrImportant; dwSizeImage = lpBf->bfSize - lpBf->bfOffBits; wsprintf(str, "wx = %d, wy = %d\nwBitCount = %d, dwClrUsed = %d, dwClrImportant = %d", wx, wy, wBitCount, dwClrUsed, dwClrImportant); MessageBox(hWnd, str, "BMP情報その2", MB_OK); { HDC hdc; HANDLE hMem; char *szBuffer; BITMAPINFO bmp_info; bmp_info.bmiHeader = *lpBi; hMem = GlobalAlloc(GHND, dwFileSize - sizeof(BITMAPFILEHEADER)); szBuffer = (char *)GlobalLock(hMem); SetFilePointer(hF, sizeof(BITMAPFILEHEADER), 0, FILE_BEGIN); ReadFile(hF, szBuffer, sizeof(BITMAPINFOHEADER) + dwSizeImage, &dwResult, NULL); hdc = GetDC(hWnd); SetDIBitsToDevice(hdc, 0, 0, //転送先座標 wx, wy, //幅、高さ 0, 0, //転送元座標 0, wy, //走査開始番号、走査線の本数 //ビットマップデータ開始のアドレス (char *)szBuffer + dwOffBits - sizeof(BITMAPFILEHEADER), &bmp_info, //BITMAPINFO構造体へのポインタ DIB_RGB_COLORS); GlobalUnlock(hMem); GlobalFree(hMem); ReleaseDC(hWnd, hdc); } GlobalUnlock(hMem1); GlobalFree(hMem1); GlobalUnlock(hMem2); GlobalFree(hMem2); CloseHandle(hF); return 0; }
次にSetFilePointer関数でファイルポインタをファイルの先頭からBITMAPFILEHEADERの分だけ 進めた位置にしておきます。そして、ReadFile関数でバッファに読みこみます。 読みこむ大きさは、BITMAPINFOHEADERとビット配列となります(今回はカラーテーブルは ない)。これが「パック済みDIBメモリフォーマット」となります。
あとは、SetDIBitsToDevice関数を実行します。ビットマップデータの開始アドレス に注意して下さい。筆者は最初間違ってszBufferの位置からdwOffBitsを加えたものにしていました。 しかし、dwOffBitsはファイルの先頭からビットマップデータまでのオフセットです。 メモリ中にはBITMAPFILEHEADER構造体は含まれていないのでポインタをその分戻さなくてはいけません。
これで、何とかビットマップを表示できるようになりました。 しかし、これはあくまでも仮のプログラムです。いろいろ問題点があります。
今回作ったプログラムで、フルカラーのビットマップファイルを表示したところです。 今回のプログラムでは表示をWM_PAINTのところで行っていないので、ウィンドウサイズを 変えると表示したグラフィックスが消えてしまいます。あらかじめウィンドウサイズを 変えてから読みこみます。ちょっと手直しをしてこの問題を解決してみて下さい。
あ、ReadDIB関数だけでなくてウィンドウのタイトルも変更していました。
Update 13/Jan/1999 By Y.Kumei