簡易BMPビュアー

今回は、BMPファイルのビュアーを作ります。そのためには、まず、BMPファイルの構造を 知らねばなりません。 一般的なBMPファイルは
BITMAPFILEHEADER
BITMAPINFOHEADER
RGBQUADの配列
イメージ本体を格納したバイト列
のような構造になっています。それぞれの内容をさらに詳しく見ると
typedef struct tagBITMAPFILEHEADER {
    WORD    bfType;     /*ファイル種別 'BM' */
    DWORD   bfSize;     /*ファイルサイズ*/
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;  /*ファイル先頭からイメージ本体までのオフセット*/
} BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER {    /* bmih */
    DWORD   biSize;  /*この構造体のサイズ*/
    LONG    biWidth; /*幅(ドット数)*/
    LONG    biHeight;/*高さ(ドット数)*/
    WORD    biPlanes;/*必ず1*/
    WORD    biBitCount;/*ピクセル当たりのビット数 1,2,4,8,16,24,32*/
    DWORD   biCompression;/*圧縮タイプ BI_RGBなら無圧縮*/
    DWORD   biSizeImage;/*イメージ本体のサイズ*/
    LONG    biXPelsPerMeter;/*水平解像度 0で良い*/
    LONG    biYPelsPerMeter;/*垂直解像度 0で良い*/
    DWORD   biClrUsed;/*使われている色の数*/
    DWORD   biClrImportant;/*重要な色の数*/
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD {     /* rgbq */
    BYTE    rgbBlue;
    BYTE    rgbGreen;
    BYTE    rgbRed;
    BYTE    rgbReserved;
} RGBQUAD;
これはパレット情報です。16ビットや24ビットや32ビットのbmpには存在しません。 8ビット以下のBMPファイルには、その色数ぶんのエントリが存在します。

イメージ本体
1ヒットから32ビットまで種類がありますが、要するに実際のイメージがベタで記録されます。(無圧縮の場合) ただし、一ラインごとに32ビット境界に合わせて「つめもの」がつめられます。

では実際に作ってみます。 「bmpv」の全ソース

ソースに従って、処理内容を解説してみます。

読込み

BITMAPFILEHEADERの情報をもとに、ファイル全体をメモリに読み込みます。 16ビットのコンパイラを使う場合、64KBより巨大なデータの取り扱いには 注意が必要です。具体的には、malloc()や_lread()等をそのまま 使うことはできません。代わりにGlobalAlloc()や_hread()を使います。

また、今回は使いませんでしたが、プログラム中でビットマップデータを直接 操作するためには、hugeなポインタを宣言してこれを使う必要があります。 メモリ操作関数もC標準のmemset()やmemcpy()は使えなくなります。

32ビットの場合は単純にmalloc()等を使えば良いのですが、ソース共通にするために #ifdefで切り換えています。

表示

BMPファイルをメモリに読み込んだものは、DIB(デバイスに依存しないビットマップ) と呼ばれます。これを画面に表示しなければなりません。 これにはいくつかの方法があります。
その1
SetDIBitsToDevice()もしくはStretchDIBits()を使います。これはDIBを 直接デバイスコンテキストに表示可能なAPIですので、文字通りAPI一発で表示 できます。SetDIBitsToDevice()は等倍です。StretchDIBits()は自由な拡大率 と縦横比で表示できますが、今回はウィンドウサイズに合わせました。
その2
一旦DDBに変換する方法もあります。処理は複雑ですが、実は、こちらのほうが高速ですし 柔軟性も上がります。手順としては、まずBMPを読み込んだタイミングで
	hdc=GetDC(hDlg);
	hMemDC = CreateCompatibleDC(hdc); /* メモリDCを作成 */
	hDDBitmap = CreateCompatibleBitmap(hdc, lpbi->biWidth, lpbi->biHeight);/*DDBを作成*/
	/* 作成したDDBをDIBにあわせて設定、走査線のコピーも行う */
	SetDIBits(hdc, hDDBitmap, 0, lpbi->biHeight, (LPSTR)lpbi+offBits, lpbi, DIB_RGB_COLORS);
	hOldBitmap = (HBITMAP)SelectObject(hMemDC, hDDBitmap);/*メモリDCにDDBを選択させる*/
	ReleaseDC(hDlg, hdc);
のようにDDBを作成しDIBの内容を転送します。前回説明したDDBの作成に、SetDIBits()が加わりました。 (順序を間違えないように。SelectObjectの前にSetDIBits()します)

表示は、前回と同じです。StretchBits()を使えば拡大/縮小もできます。

	case WM_PAINT:
		hdc = BeginPaint(hDlg, &ps);
		BitBlt(hdc, 0, 0, lpbi->biWidth, lpbi->biHeight, hMemDC, 0, 0, SRCCOPY);
		EndPaint(hDlg, &ps);
もちろん、必要なくなったら削除します。
	SelectObject(hMemDC, hOldBitmap);
	DeleteDC(hMemDC);
	DeleteObject(hDDBitmap);
これを新たなBMPを読み込むたびにやる必要があります。
その3
DIB Sectionというものを使う方法がありますが、 私はこの方法を使ったことがないため、今回は省略します。

ところで、今回作ったBMPビュアーには、まずい点が二点ほどあります。 まず、画面モードが256色モードで色がおかしくなる点(パレットを考慮していない)。 次に、BMPがウィンドウサイズをはみ出してもスクロールもしない点です。 これらの欠点の改善は、そのうち気が向いたらやるかもしれません。

前のページ

次のページ

一つ上のページに戻る