dポイントプレゼントキャンペーン実施中!

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);

}

どこか改善点などありましたら、よろしくお願いいたします。

A 回答 (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 …

 一応クラス階層図中にもCPictureBoxなるものは無い見たいです。
 http://msdn.microsoft.com/ja-jp/library/ws8s10w4 …
    • good
    • 0

 こんばんは。

補足頂きました。
 はい、ありません。
 と、言うのも、

 //ビットマップの情報を取る
 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);

この回答への補足

ご回答ありがとうございます。
変更点を直したのですが、やはりまだうまくいかないようです。すみません。

下が修正したプログラムです。

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型でよろしかったでしょうか。

たびたび本当に申し訳ないのですが、ご指導よろしくお願いいたします。

補足日時:2009/02/13 20:22
    • good
    • 0

 こんにちは。


 すんません、どうやらファイルサイズの設定が疎かになっていた様です。コレで如何にか成りませんか。

//以上同じ

//ファイルヘッダとビットマップヘッダ
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);

//以下同じ

この回答への補足

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

補足日時:2009/02/13 18:54
    • good
    • 0

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;
}
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!