ディスク上のビットマップをピクチャーボックスへ表示


【ピクチャーボックスのバンドル取得】
リソース管理された画像を標準の手順でピクチャーボックスへ出力するためには、

  CDC* pDC = m_pict.GetDC();
  CDC  myDC;
  CBitmap myBMP;
  myBMP.LoadBitmap(IDB_BITMAP_MORI);
  myDC.CreateCompatibleDC(pDC);
  CBitmap* oldBMP = myDC.SelectObject(&myBMP);
  pDC->BitBlt(0,0,256,256,&myDC,0,0,SRCCOPY);

といった記述を使います。GetDC()で取得したCDC型のデバイスコンテキストを出発先
指定に使います。とこらが今、記述しようとしている方法ではAPI関数を使うので、

  HBITMAP myDIB;          //ビットマップのハンドル
  HDC hDC;                //デバイスコンテキストのハンドル
  HDC hCompatiDC;         //メモリデバイスコンテキスト
  PAINTSTRUCT ps;         //クライアント領域の描画に使う情報を保持

  hDC = ::GetDC(m_hWnd);

  ::BeginPaint(m_hWnd, &ps);
  hCompatiDC = ::CreateCompatibleDC(hDC);
  myDIB = ::CreateDIBitmap(hDC, &m_BmpInfo->bmiHeader,
          CBM_INIT, m_BmpImage, m_BmpInfo, DIB_RGB_COLORS);
  ::SelectObject(hCompatiDC, myDIB);
  BitBlt(hDC, 5, 30, m_BmpInfo->bmiHeader.biWidth,
          m_BmpInfo->bmiHeader.biHeight, hCompatiDC, 0, 0, SRCCOPY);
  ::EndPaint(m_hWnd, &ps);

のように、HDC型のハンドルであるhDCを使います。ここで2GetDC関数を比較する
と次のようになります。

  MFC : CDC* GetDC();          //CWnd::GetDCの書式
  API : HDC GetDC(HWND hWnd);  //API関数であるGetDCの書式

このように、戻り値に違いがあります。そこで、MFCを使って

  CDC* pDC = m_pict.GetDC();

で求めたpDCは使用できません。正しい処理をするためには、

  CDC*型のpDCからHDC型のハンドルを取り出す

という作業が必要です。従って、以下のように記述することでHDC型のハンドルを
取得できます。

  HDC hDC;
  hDC = m_pict.GetDC()->GetSafeHdc();

(備考)
    GetSafeHdc関数 : デバイスコンテキストからハンドルを取得

【サンプル・ソース】
//-----------------------------------------
public:
	char* m_BmpImage;
	LPBITMAPINFO m_BmpInfo;
	BITMAPFILEHEADER m_BmpFileHdr;

COpenviewsapDlg::COpenviewsapDlg(CWnd* pParent /*=NULL*/)
	: CDialog(COpenviewsapDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(COpenviewsapDlg)
		// メモ: この位置に ClassWizard によってメンバの初期化が追加されます。
	//}}AFX_DATA_INIT
	// メモ: LoadIcon は Win32 の DestroyIcon のサブシーケンスを要求しません。
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	//---------------------------------------------
	m_BmpInfo = NULL;
	m_BmpImage = NULL;
}

void COpenviewsapDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // 描画用のデバイス コンテキスト

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// クライアントの矩形領域内の中央
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// アイコンを描画します。
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
		//--------------------------------------------------------
		HBITMAP myDIB;          //ビットマップのハンドル
		HDC hDC;                //デバイスコンテキストのハンドル
		HDC hCompatiDC;         //メモリデバイスコンテキスト
		PAINTSTRUCT ps;         //クライアント領域描画用

		//-------------------------------
		//画像の準備ができれいるかの判定
		if(!m_BmpInfo)return;

		//--------------------------------------------------------------------------
		//ピクチャーボックスへの出力(ID = IDC_PICT としメソッド変数を m_pict と設定)
		hDC = m_pict.GetDC()->GetSafeHdc();

		//--------------------------------------------------
		//画像の描画準備
		//描画が終了したら必ずEndPaintを実行する必要がある
		::BeginPaint(m_hWnd,&ps);

		//---------------------------------------------------
		//hDCと互換性のあるメモリデバイスコンテキストを作成
		hCompatiDC = ::CreateCompatibleDC(hDC);

		//-------------------------------------------------------------
		//DIB形式ビットマップからDDB(デバイス依存)形式ビットマップ作成
		myDIB = ::CreateDIBitmap(hDC,&m_BmpInfo->bmiHeader,
			      CBM_INIT,m_BmpImage,m_BmpInfo,DIB_RGB_COLORS);

		//--------------------------------------------------
		//hCompatiDCでmyDIBの示すオプションを使用可能にする
		::SelectObject(hCompatiDC, myDIB);

		//-------------------------------------------------
		//hCompatiDCにある画像をhDCに表示する
		BitBlt(hDC,0,0,m_BmpInfo->bmiHeader.biWidth,
			m_BmpInfo->bmiHeader.biHeight,hCompatiDC,0,0,SRCCOPY);

		//------------------------------------------------
		//BeginPaint対応処理(描画の後はEndPaintが必要)
		::EndPaint(m_hWnd,&ps);

		//----------------------------------
		//オブジェクトの解放
		::DeleteObject(myDIB);
		::DeleteObject(hCompatiDC);
		::ReleaseDC(m_hWnd,hDC);

	}
}

void COpenviewsapDlg::OnButton1() 
{
	// TODO: この位置にコントロール通知ハンドラ用のコードを追加してください
    CString filename;
	CFile file;
	BITMAPINFOHEADER myBmpInfoHdr;

	//ファイルダイアログを表示してファイル指定する
	CFileDialog myDLG(TRUE,NULL,NULL,
		OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
		"ビットマップ(*.BMP)|*.BMP||");
	if(myDLG.DoModal() != IDOK)return;

	//取得したファイルをオープンする
	filename = myDLG.GetPathName();
	if(!file.Open(filename,CFile::modeRead|CFile::typeBinary)){
		return;
	}

	//タイトルバーにファイル名を表示する
    SetWindowText(filename);

	//ファイルヘッダ部とインフォヘッダ部を読み込みます
	file.Read(&m_BmpFileHdr,sizeof(BITMAPFILEHEADER));
	file.Read(&myBmpInfoHdr,sizeof(BITMAPINFOHEADER));

	//前回を使用していれば、一旦メモリを解放する
	if(m_BmpInfo) delete[] m_BmpInfo;

	//色情報を取得する
	//biBitCountは1ビクセルあたりのカラー表現ビット数
	//1,4,8,16,24,32がある。数字が大きいほど情報表現が可能
	//16ビット以上と未満でカラーデータ格納が異なる
	if(myBmpInfoHdr.biBitCount >= 16){
		m_BmpInfo = (LPBITMAPINFO)new char[sizeof(BITMAPINFO)];
	}
	else{
		m_BmpInfo = (LPBITMAPINFO)new char[sizeof(BITMAPINFOHEADER) +
			        (1 << myBmpInfoHdr.biBitCount) * sizeof(RGBQUAD)];
		file.Read(m_BmpInfo->bmiColors,
			        (1 << myBmpInfoHdr.biBitCount) * sizeof(RGBQUAD));
	}

	//m_BmpInfo(LPBITMAPINFO型)のmyBmpInfoHdrメンバに設定
	m_BmpInfo->bmiHeader = myBmpInfoHdr;

	//ファイル内のビットマップ実データ言いに合わせる
	file.Seek(m_BmpFileHdr.bfOffBits,CFile::begin);

	//前回の画像イメージを一旦解放
	if(m_BmpImage) delete[] m_BmpImage;

	//実画像データ分のバイト数を確保
	//bfsizeビットマップ・ファイル全サイズ
	//bfOffBits先頭にあるヘッダ情報サイズ
	m_BmpImage = new char[m_BmpFileHdr.bfSize - m_BmpFileHdr.bfOffBits];

	//ビットマップ実データを読み込み格納
	file.Read(m_BmpImage,m_BmpFileHdr.bfSize - m_BmpFileHdr.bfOffBits);
	file.Close();

	//再描画表示
	Invalidate();
}