ディスク上のBMPファイルを表示する

ディスク上のビットマップ(BMP)ファイルは、Win32API対応のデバイス独立ビットマップ(DIB形式)で格納されている。 MFCではこの形式のファイルを読み込む方法が用意されていないため、自分で用意しなければならない。
まず、このDIB形式について以下に表示してみる。

DIB形式
ファイルヘッダ
BITMAPFILEHEADER構造体
情報ヘッダ
BITMAPINFOHEADER構造体
カラーデータ
複数個のRGBQUAD構造体
ピクセルデータ

以下に順を追ってDIB形式ファイルを表示する方法を記述する。


1. DIB形式ファイルの読み込み
ファイルをオープンしたら、メモリを確保してデータを読み込む。ファイルの大きさは、CFile::GetLength()で取得できる。
	m_pDib = (BYTE*)new char[file.GetLength()];
	file.Read(m_pDib, file.GetLength());
データを読み込んだら、まずBITMAPFILEHEADER構造体へのポインタを取得し、 読み込んだファイルの形式が正しいかチェックする。 DIB形式のファイルでは、BITMAPFILEHEADER構造体のbfTypeメンバが"BM"という文字列になっている。
	m_pBMFH = (BITMAPFILEHEADER*)m_pDib;
	if( m_pBMFH->bfType != *((WORD*)"BM")) {
		……  // ファイル形式が正しくない場合の処理
	}
その後、各構造体へのポインタを設定する。
	m_pBMIH = (BITMAPINFOHEADER*)(m_pDib + sizeof(BITMAPFILEHEADER));
	m_pBMI = (BITMAPINFO*)m_pBMIH;
	m_pData = m_pDib + m_pBMFH->bfOffBits;
BITMAPINFO構造体は、BITMAPINFOHEADER構造体とRGBQUAD構造体をまとめたものである。 また、m_pDataはピクセルデータ領域へのポインタである。BITMAPFILEHEADER構造体のbfOffBitsメンバは、 DIB形式ファイルの先頭からピクセルデータ領域の先頭位置までのオフセットを示している。


2. DIB形式ファイルの表示
DIB形式のファイルを表示するにはいくつかの方法があるが、ここではAPI関数であるStretchDIBits関数を使用する。
	::StretchDIBits(pDC->GetSafeHdc(),
		x, y, m_pBMIH->biWidth, m_pBMIH->biHeight,  // 転送先
		0, 0, m_pBMIH->biWidth, m_pBMIH->biHeight,  // 転送元
		m_pData, m_pBMI, DIB_RGB_COLORS, SRCCOPY);

各構造体の定義
BITMAPFILEHEADER構造体
この構造体の大きさは14バイトである。
typedef struct tagBITMAPFILEHEADER {
	WORD	bfType;		// 常に"BM"
	DWORD	bfSize;		// ファイルサイズ
	WORD	bfReserved1;	// 0に設定
	WORD	bfReserved2;	// 0に設定
	DWORD	bfOffBits;	// DIB形式ファイルの先頭からピクセルデータ領域の先頭までのオフセット
} BITMAPFILEHEADER;
BITMAPINFOHEADER構造体
この構造体の大きさは40バイトである。
typedef struct tagBITMAPINFOHEADER {
	DWORD	biSize;		// この構造体のサイズ
	DWORD	biWidth;		// 幅(ピクセル単位)
	DWORD	biHeight;		// 高さ(ピクセル単位)
	WORD	biPlanes;		// 常に1
	WORD	biBitCount;	// 1ピクセルあたりのカラービットの数
	DWORD	biCompression;	// BI_RGB, BI_RLE8, BI_RLE4のいずれか
	DWORD	biSizeImage;	// イメージの全バイト数
	DWORD	biXPelsPerMeter;	// 0または水平解像度
	DWORD	biYPelsPerMeter;	// 0または垂直解像度
	DWORD	biClrUsed;	// 通常は0、biBitCount以下のカラー数に設定可能
	DWORD	biClrImportant;	// 通常は0
} BITMAPINFOHEADER;
RGBQUAD構造体
この構造体(大きさは4バイト)はビットマップのカラーデータを表すものだが、 1つの構造体で1つのカラーデータを表している。 つまり、16色なら16個、256色なら256個のRGBQUAD構造体が並んでいる。
BITMAPFILEHEADER構造体のbfOffBitsの値はBITMAPINFOHEADER構造体のbiBitCountの値によって決定される。 例えばbiBitcount = 8の場合、そのビットマップは256色であるからRGB構造体は256個存在する。 よってbfOffBits = 14 + 40 + 4 * 256 = 1078 となる。
但し、16ビット(65536色)以上の場合はカラーパレットが不要なため、この構造体は使用されない。常にbfOffBits = 54である。
typedef struct tagRGBQUAD {
	BYTE	rgbBlue;		// 青の輝度(0〜255)
	BYTE	rgbGreen;		// 緑の輝度(0〜255)
	BYTE	rgbRed;		// 赤の輝度(0〜255)
	BYTE	rgbReserved;	// 予約値。0にする
} RGBQUAD;
BITMAPINFO構造体
typedef struct tagBITMAPINFO {
	BITMAPINFOHEADER	bmiHeader;
	RGBQUAD		bmiColors[1];
} BITMAPINFO;

DIB形式ファイルの読み込み、表示用自作クラスの実装

ここではDIB形式ファイルの読み込みと表示を行うために作成したクラスCUKDibの実装を以下に記述する。 実用するには、ファイル例外処理などのエラー処理を追加しておきたい。

// ヘッダファイルの記述 --------------------
class CUKDib : public CObject
{
public:
	CUKDib();
	virtual ~CUKDib();

	BOOL Open(const char* pzFileName = NULL);
	void Close();
	void Draw(CDC* pDC, int x = 0, int y = 0);

private:
	BYTE*		m_pDib;
	BITMAPFILEHEADER*	m_pBMFH;
	BITMAPINFOHEADER*	m_pBMIH;
	BITMAPINFO*	m_pBMI;
	BYTE*		m_pData;
};


// ソースファイルの記述 --------------------
#include "stdafx.h"
#include "UKDib.h"

/////////////////////////////////////////////////////////////////////////////
// コンストラクタ
/////////////////////////////////////////////////////////////////////////////
CUKDib::CUKDib()
{
	m_pDib = NULL;
}

/////////////////////////////////////////////////////////////////////////////
// デストラクタ
/////////////////////////////////////////////////////////////////////////////
CUKDib::~CUKDib()
{
	Close();
}

/////////////////////////////////////////////////////////////////////////////
// Close関数
// 終了処理
//
// 引数:なし
// 戻り値:なし
// ※ メモリにデータが残っている場合は解放する
/////////////////////////////////////////////////////////////////////////////
void CUKDib::Close()
{
	if( m_pDib != NULL ) {
		delete m_pDib;
		m_pDib = NULL;
	}
}

/////////////////////////////////////////////////////////////////////////////
// Open関数
// DIB形式のビットマップファイルを開き、データを読み込む
//
// 引数:
//    pzFileName: ビットマップファイル名(デフォルトはNULL)
// 戻り値:
//    データの読み込みに成功した場合はTRUE、失敗時はFALSE
/////////////////////////////////////////////////////////////////////////////
BOOL CUKDib::Open(const char* pzFileName /* =NULL */)
{
	CFile file;
	CString sFileName;

	// あらかじめメモリを空にしておく
	Close();

	// ファイル名の決定
	// 引数に指定されていればそれを使用する。指定されていない場合は
	// コモンファイルダイアログを開く
	if( pzFileName == NULL ) {
		CFileDialog dlg(TRUE, "bmp", NULL,
			OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,
			"ビットマップファイル(*.bmp)|*.bmp|全てのファイル|*.*|", NULL);
		if( dlg.DoModal() == IDOK ) {
			sFileName = dlg.GetPathName();
		}
		else {
			Close();
			return FALSE;
		}
	}
	else
		sFileName = pzFileName;

	// ファイルのオープン
	if( file.Open(sFileName, CFile::modeRead | CFile::typeBinary) == FALSE ) {
		Close();
		return FALSE;
	}

	// メモリを確保し、データを読み込む
	m_pDib = (BYTE*)new char[file.GetLength()];
	file.Read(m_pDib, file.GetLength());

	// BITMAPINFOHEADER構造体の取得
	m_pBMFH = (BITMAPFILEHEADER*)m_pDib;

	// 読み込んだファイルの形式をチェック
	if( m_pBMFH->bfType != *((WORD*)"BM")) {
		AfxMessageBox("読み込んだファイルの形式が正しくありません。");
		file.Close();
		Close();
		return FALSE;
	}

	// その他の構造体やデータ領域へのポインタの設定
	m_pBMIH = (BITMAPINFOHEADER*)(m_pDib + sizeof(BITMAPFILEHEADER));
	m_pBMI = (BITMAPINFO*)m_pBMIH;
	m_pData = m_pDib + m_pBMFH->bfOffBits;

	// ファイルのクローズと終了(正常終了)
	file.Close();
	return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
// Draw関数
// 読み込んだビットマップファイルを表示する
//
// 引数:
//    pDC: デバイスコンテキスト
//    x: ビットマップの左上隅のx座標
//    y: ビットマップの左上隅のy座標
// 戻り値:
//    なし
/////////////////////////////////////////////////////////////////////////////
void CUKDib::Draw(CDC* pDC, int x, int y)
{
	::StretchDIBits(pDC->GetSafeHdc(),
		x, y, m_pBMIH->biWidth, m_pBMIH->biHeight,  // 転送先
		0, 0, m_pBMIH->biWidth, m_pBMIH->biHeight,  // 転送元
		m_pData, m_pBMI, DIB_RGB_COLORS, SRCCOPY);
}

目次へ