• 締切済み

bitmap画像の保存がうまくいきません。

VC++のMFC、ダイアログベースで画像処理のソフトを作っています。 処理した画像を保存したいのですが、「描画できませんでした」というメッセージのでるファイルになってしまい、うまく保存できません。 プログラムは以下のようになっていてピクチャーコントロールの変数をm_pict8にしています。また、画像は24ビットで240×320のものを保存します。 static LONG CalcScanLineByte(const LONG w, const WORD bpp) { return (((bpp * w) + 31) / 32) * 4; } //----------------------------------------------------------- void Cstart2Dlg::OnBnClickedButton10() //保存ボタン { CFileDialog myDLG(FALSE,"BMP","*.BMP",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"画像(*.BMP)|*.BMP||"); if(myDLG.DoModal() == IDOK){ CStdioFile fout(myDLG.GetPathName(),CFile::modeCreate | CFile::modeWrite|CFile::typeBinary); //ピクチャボックスからビットマップを取り出す HBITMAP hBitmap = m_pict8.GetBitmap(); //無いので処理できない if(hBitmap == NULL)return; //ビットマップの情報を取る BITMAP bitmap = {0}; ::GetObject(hBitmap, sizeof(bitmap), &bitmap); //4バイト調整したスキャンラインのサイズ const int iScanLineByte = ::CalcScanLineByte(240, bitmap.bmBitsPixel); //const int iScanLineByte = ::CalcScanLineByte(bitmap.bmWidth, bitmap.bmBitsPixel); //ファイルヘッダとビットマップヘッダ BITMAPFILEHEADER bmfh = {sizeof(bmfh)}; BITMAPINFOHEADER bmif = {sizeof(bmif)}; //ビットマップである事を示す名称 bmfh.bfType = ('M' << 8) | 'B'; //イメージデータへのオフセットはファイルヘッダ+ビットマップヘッダ bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmif); //見ての通り bmif.biBitCount = 24; //24ビットの時はBI_RGBで固定 bmif.biCompression = BI_RGB; //常に1で固定 bmif.biPlanes = 1; //見ての通り bmif.biWidth = 240; bmif.biHeight= 320; //bmif.biWidth = bitmap.bmWidth; //bmif.biHeight= bitmap.bmHeight; //4バイト調整も含めた正確な合計バイトサイズが必要 bmif.biSizeImage = iScanLineByte * bmif.biHeight; //ファイルヘッダ→ビットマップヘッダの順番に書き出す fout.Write(&bmfh, sizeof(bmfh)); fout.Write(&bmif, sizeof(bmif)); //イメージデータをセーブする HDC hMemDC = ::CreateCompatibleDC(NULL); ::SelectObject(hMemDC, hBitmap); //yを縦幅-1から回転させないと上下逆転してしまう for(int y = bmif.biHeight - 1; y >= 0; --y) { //3バイトずつステップする for(int x = 0; x < iScanLineByte; x += 3) { const COLORREF cref = ::GetPixel(hMemDC, x / 3, y); //色素の位置関係をBGRにしないと赤と青の関係が逆転してしまう const BYTE arrBy[3] = {GetBValue(cref), GetGValue(cref), GetRValue(cref)}; //1ピクセル分(3バイト)書き出す fout.Write(arrBy, sizeof(arrBy)); } } ::DeleteDC(hMemDC); } どこか改善点などありましたら、よろしくお願いいたします。

みんなの回答

回答No.4

 こんばんは。補足頂きました。  はい。当方の方では、其のコードで書き込めています。  一応、環境を出して置きます。win2000sp4 vc6.0sp5です。  読み込み確認に使用したのは、御馴染みのMSペイントブラシとMSOfficeピクチャーマネージャーです。  う~む。では確認です。以下は通過出来ていますか。hBitmapの中がNULLではないでしょうか。  //無いので処理できない  if(hBitmap == NULL)return;  其の他、デバッガでステップトレースして、怪しいと思う部分はありませんでしょうか。 >>あとピクチャーコントロールの変数m_pict8はCStatic型でよろしかったでしょうか。  Cstart2Dlgのヘッダでm_pict8が何型で宣言されていますでしょうか。CStaticの派生クラス辺りでしょうか。  MFCですから、CStaticコントロールでタイプがビットマップになったものがピクチャーボックスではないでしょうか。  http://msdn.microsoft.com/ja-jp/library/b7w5x74z(VS.80).aspx  一応クラス階層図中にもCPictureBoxなるものは無い見たいです。  http://msdn.microsoft.com/ja-jp/library/ws8s10w4(VS.80).aspx

回答No.3

 こんばんは。補足頂きました。  はい、ありません。  と、言うのも、  //ビットマップの情報を取る  BITMAP bitmap = {0};  ::GetObject(hBitmap, sizeof(bitmap), &bitmap);  bitmap.bmWidth//横幅が入っている  bitmap.bmHeight//縦幅が入っている  bitmap.bmBitsPixel//ビット数が入っている  仮にビットマップが240x320x24であろうと1440x900x32であろうとGetObject()で情報を検出出来るからです(実際に前半で情報を検出しています)。  更に、度々すんません、「イメージデータをセーブする」の所を、以下に訂正して下さい。其のままですと縦横が奇数の時(241x319x24で確認)、正常にイメージデータが書けません。 //イメージデータをセーブする LPBYTE pBuf = (LPBYTE)::calloc(bmif.biSizeImage, 1); HDC hMemDC = ::CreateCompatibleDC(NULL); ::SelectObject(hMemDC, hBitmap); for(int y = bmif.biHeight - 1; y >= 0; --y) { for(int x = 0; x < bmif.biWidth; ++x) { const int pos = (x * 3) + (iScanLineByte * y); const COLORREF cref = ::GetPixel(hMemDC, x, bmif.biHeight - 1 - y); pBuf[pos + 0] = GetBValue(cref); pBuf[pos + 1] = GetGValue(cref); pBuf[pos + 2] = GetRValue(cref); } } ::DeleteDC(hMemDC); fout.Write(pBuf, bmif.biSizeImage); ::free(pBuf);

s0511146
質問者

補足

ご回答ありがとうございます。 変更点を直したのですが、やはりまだうまくいかないようです。すみません。 下が修正したプログラムです。 static LONG CalcScanLineByte(const LONG w, const WORD bpp) { return (((bpp * w) + 31) / 32) * 4; } //----------------------------------------------------------- void Cstart2Dlg::OnBnClickedButton10() { CFileDialog myDLG(FALSE,"BMP","*.BMP",OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,"画像(*.BMP)|*.BMP||"); if(myDLG.DoModal() == IDOK){ CStdioFile fout(myDLG.GetPathName(),CFile::modeCreate | CFile::modeWrite|CFile::typeBinary); //ピクチャボックスからビットマップを取り出す HBITMAP hBitmap = m_pict8.GetBitmap(); //無いので処理できない if(hBitmap == NULL)return; //ビットマップの情報を取る BITMAP bitmap = {0}; ::GetObject(hBitmap, sizeof(bitmap), &bitmap); //4バイト調整したスキャンラインのサイズ const int iScanLineByte = ::CalcScanLineByte(bitmap.bmWidth, bitmap.bmBitsPixel); //ファイルヘッダとビットマップヘッダ BITMAPFILEHEADER bmfh = {(0)}; BITMAPINFOHEADER bmif = {sizeof(bmif)}; //ビットマップである事を示す名称 bmfh.bfType = ('M' << 8) | 'B'; //イメージデータへのオフセットはファイルヘッダ+ビットマップヘッダ bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmif); //このイメージファイルのサイズ(コレを追加) bmfh.bfSize = bmfh.bfOffBits + (iScanLineByte * bitmap.bmHeight); //見ての通り bmif.biBitCount = 24; //24ビットの時はBI_RGBで固定 bmif.biCompression = BI_RGB; //常に1で固定 bmif.biPlanes = 1; //見ての通り bmif.biWidth = bitmap.bmWidth; bmif.biHeight= bitmap.bmHeight; //4バイト調整も含めた正確な合計バイトサイズが必要 bmif.biSizeImage = iScanLineByte * bmif.biHeight; //ファイルヘッダ→ビットマップヘッダの順番に書き出す fout.Write(&bmfh, sizeof(bmfh)); fout.Write(&bmif, sizeof(bmif)); //イメージデータをセーブする LPBYTE pBuf = (LPBYTE)::calloc(bmif.biSizeImage, 1); HDC hMemDC = ::CreateCompatibleDC(NULL); ::SelectObject(hMemDC, hBitmap); for(int y = bmif.biHeight - 1; y >= 0; --y) { for(int x = 0; x < bmif.biWidth; ++x) { const int pos = (x * 3) + (iScanLineByte * y); const COLORREF cref = ::GetPixel(hMemDC, x, bmif.biHeight - 1 - y); pBuf[pos + 0] = GetBValue(cref); pBuf[pos + 1] = GetGValue(cref); pBuf[pos + 2] = GetRValue(cref); } } ::DeleteDC(hMemDC); fout.Write(pBuf, bmif.biSizeImage); ::free(pBuf); } ファイルは出来るのですが、0×0のファイルが出来てしまいます。ビットの深さは24ビットになっているようですが…。 あとピクチャーコントロールの変数m_pict8はCStatic型でよろしかったでしょうか。 たびたび本当に申し訳ないのですが、ご指導よろしくお願いいたします。

回答No.2

 こんにちは。  すんません、どうやらファイルサイズの設定が疎かになっていた様です。コレで如何にか成りませんか。 //以上同じ //ファイルヘッダとビットマップヘッダ BITMAPFILEHEADER bmfh = {0};//ココを変更 BITMAPINFOHEADER bmif = {sizeof(bmif)}; //ビットマップである事を示す名称 bmfh.bfType = ('M' << 8) | 'B'; //イメージデータへのオフセットはファイルヘッダ+ビットマップヘッダ bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmif); //このイメージファイルのサイズ(コレを追加) bmfh.bfSize = bmfh.bfOffBits + (iScanLineByte * bitmap.bmHeight); //以下同じ

s0511146
質問者

補足

早速のご回答ありがとうございます。 変更してやってみたのですがうまくいきませんでした。 自分なりに考えてみたのですが、 bitmap.bmWidthとbitmap.bmHeightを今回保存したい画像のサイズ240×320なので、 それぞれ240と320に変える必要はありませんでしょうか?

  • php504
  • ベストアンサー率42% (926/2160)
回答No.1

CalcScanLineByte( )関数が変じゃないですか 画像1行分のバイト数を求めるんですよね bppを8で割って1ピクセルのバイト数を求めそれと横幅のwをかけて1行分のバイト数を求める。 それを4バイト境界に調整。 でいいのでは。 static LONG CalcScanLineByte(const LONG w, const WORD bpp) { LONG line = w * ( bpp / 8 ); // 1行のバイト数 while (line & 3) line++; // 4バイト境界にそろえる return line; }

関連するQ&A